1 /* 2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> 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 * Development of this code funded by Astaro AG (http://www.astaro.com/) 9 */ 10 11 #include <linux/kernel.h> 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/netlink.h> 15 #include <linux/netfilter.h> 16 #include <linux/netfilter/nf_tables.h> 17 #include <linux/in.h> 18 #include <linux/ip.h> 19 #include <linux/ipv6.h> 20 #include <linux/smp.h> 21 #include <net/dst.h> 22 #include <net/sock.h> 23 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */ 24 #include <net/netfilter/nf_tables.h> 25 #include <net/netfilter/nft_meta.h> 26 27 void nft_meta_get_eval(const struct nft_expr *expr, 28 struct nft_data data[NFT_REG_MAX + 1], 29 const struct nft_pktinfo *pkt) 30 { 31 const struct nft_meta *priv = nft_expr_priv(expr); 32 const struct sk_buff *skb = pkt->skb; 33 const struct net_device *in = pkt->in, *out = pkt->out; 34 struct nft_data *dest = &data[priv->dreg]; 35 36 switch (priv->key) { 37 case NFT_META_LEN: 38 dest->data[0] = skb->len; 39 break; 40 case NFT_META_PROTOCOL: 41 *(__be16 *)dest->data = skb->protocol; 42 break; 43 case NFT_META_NFPROTO: 44 dest->data[0] = pkt->ops->pf; 45 break; 46 case NFT_META_L4PROTO: 47 dest->data[0] = pkt->tprot; 48 break; 49 case NFT_META_PRIORITY: 50 dest->data[0] = skb->priority; 51 break; 52 case NFT_META_MARK: 53 dest->data[0] = skb->mark; 54 break; 55 case NFT_META_IIF: 56 if (in == NULL) 57 goto err; 58 dest->data[0] = in->ifindex; 59 break; 60 case NFT_META_OIF: 61 if (out == NULL) 62 goto err; 63 dest->data[0] = out->ifindex; 64 break; 65 case NFT_META_IIFNAME: 66 if (in == NULL) 67 goto err; 68 strncpy((char *)dest->data, in->name, sizeof(dest->data)); 69 break; 70 case NFT_META_OIFNAME: 71 if (out == NULL) 72 goto err; 73 strncpy((char *)dest->data, out->name, sizeof(dest->data)); 74 break; 75 case NFT_META_IIFTYPE: 76 if (in == NULL) 77 goto err; 78 *(u16 *)dest->data = in->type; 79 break; 80 case NFT_META_OIFTYPE: 81 if (out == NULL) 82 goto err; 83 *(u16 *)dest->data = out->type; 84 break; 85 case NFT_META_SKUID: 86 if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT) 87 goto err; 88 89 read_lock_bh(&skb->sk->sk_callback_lock); 90 if (skb->sk->sk_socket == NULL || 91 skb->sk->sk_socket->file == NULL) { 92 read_unlock_bh(&skb->sk->sk_callback_lock); 93 goto err; 94 } 95 96 dest->data[0] = 97 from_kuid_munged(&init_user_ns, 98 skb->sk->sk_socket->file->f_cred->fsuid); 99 read_unlock_bh(&skb->sk->sk_callback_lock); 100 break; 101 case NFT_META_SKGID: 102 if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT) 103 goto err; 104 105 read_lock_bh(&skb->sk->sk_callback_lock); 106 if (skb->sk->sk_socket == NULL || 107 skb->sk->sk_socket->file == NULL) { 108 read_unlock_bh(&skb->sk->sk_callback_lock); 109 goto err; 110 } 111 dest->data[0] = 112 from_kgid_munged(&init_user_ns, 113 skb->sk->sk_socket->file->f_cred->fsgid); 114 read_unlock_bh(&skb->sk->sk_callback_lock); 115 break; 116 #ifdef CONFIG_IP_ROUTE_CLASSID 117 case NFT_META_RTCLASSID: { 118 const struct dst_entry *dst = skb_dst(skb); 119 120 if (dst == NULL) 121 goto err; 122 dest->data[0] = dst->tclassid; 123 break; 124 } 125 #endif 126 #ifdef CONFIG_NETWORK_SECMARK 127 case NFT_META_SECMARK: 128 dest->data[0] = skb->secmark; 129 break; 130 #endif 131 case NFT_META_PKTTYPE: 132 if (skb->pkt_type != PACKET_LOOPBACK) { 133 dest->data[0] = skb->pkt_type; 134 break; 135 } 136 137 switch (pkt->ops->pf) { 138 case NFPROTO_IPV4: 139 if (ipv4_is_multicast(ip_hdr(skb)->daddr)) 140 dest->data[0] = PACKET_MULTICAST; 141 else 142 dest->data[0] = PACKET_BROADCAST; 143 break; 144 case NFPROTO_IPV6: 145 if (ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF) 146 dest->data[0] = PACKET_MULTICAST; 147 else 148 dest->data[0] = PACKET_BROADCAST; 149 break; 150 default: 151 WARN_ON(1); 152 goto err; 153 } 154 break; 155 case NFT_META_CPU: 156 dest->data[0] = smp_processor_id(); 157 break; 158 case NFT_META_IIFGROUP: 159 if (in == NULL) 160 goto err; 161 dest->data[0] = in->group; 162 break; 163 case NFT_META_OIFGROUP: 164 if (out == NULL) 165 goto err; 166 dest->data[0] = out->group; 167 break; 168 default: 169 WARN_ON(1); 170 goto err; 171 } 172 return; 173 174 err: 175 data[NFT_REG_VERDICT].verdict = NFT_BREAK; 176 } 177 EXPORT_SYMBOL_GPL(nft_meta_get_eval); 178 179 void nft_meta_set_eval(const struct nft_expr *expr, 180 struct nft_data data[NFT_REG_MAX + 1], 181 const struct nft_pktinfo *pkt) 182 { 183 const struct nft_meta *meta = nft_expr_priv(expr); 184 struct sk_buff *skb = pkt->skb; 185 u32 value = data[meta->sreg].data[0]; 186 187 switch (meta->key) { 188 case NFT_META_MARK: 189 skb->mark = value; 190 break; 191 case NFT_META_PRIORITY: 192 skb->priority = value; 193 break; 194 case NFT_META_NFTRACE: 195 skb->nf_trace = 1; 196 break; 197 default: 198 WARN_ON(1); 199 } 200 } 201 EXPORT_SYMBOL_GPL(nft_meta_set_eval); 202 203 const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { 204 [NFTA_META_DREG] = { .type = NLA_U32 }, 205 [NFTA_META_KEY] = { .type = NLA_U32 }, 206 [NFTA_META_SREG] = { .type = NLA_U32 }, 207 }; 208 EXPORT_SYMBOL_GPL(nft_meta_policy); 209 210 int nft_meta_get_init(const struct nft_ctx *ctx, 211 const struct nft_expr *expr, 212 const struct nlattr * const tb[]) 213 { 214 struct nft_meta *priv = nft_expr_priv(expr); 215 int err; 216 217 priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); 218 switch (priv->key) { 219 case NFT_META_LEN: 220 case NFT_META_PROTOCOL: 221 case NFT_META_NFPROTO: 222 case NFT_META_L4PROTO: 223 case NFT_META_PRIORITY: 224 case NFT_META_MARK: 225 case NFT_META_IIF: 226 case NFT_META_OIF: 227 case NFT_META_IIFNAME: 228 case NFT_META_OIFNAME: 229 case NFT_META_IIFTYPE: 230 case NFT_META_OIFTYPE: 231 case NFT_META_SKUID: 232 case NFT_META_SKGID: 233 #ifdef CONFIG_IP_ROUTE_CLASSID 234 case NFT_META_RTCLASSID: 235 #endif 236 #ifdef CONFIG_NETWORK_SECMARK 237 case NFT_META_SECMARK: 238 #endif 239 case NFT_META_PKTTYPE: 240 case NFT_META_CPU: 241 case NFT_META_IIFGROUP: 242 case NFT_META_OIFGROUP: 243 break; 244 default: 245 return -EOPNOTSUPP; 246 } 247 248 priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); 249 err = nft_validate_output_register(priv->dreg); 250 if (err < 0) 251 return err; 252 253 err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); 254 if (err < 0) 255 return err; 256 257 return 0; 258 } 259 EXPORT_SYMBOL_GPL(nft_meta_get_init); 260 261 int nft_meta_set_init(const struct nft_ctx *ctx, 262 const struct nft_expr *expr, 263 const struct nlattr * const tb[]) 264 { 265 struct nft_meta *priv = nft_expr_priv(expr); 266 int err; 267 268 priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); 269 switch (priv->key) { 270 case NFT_META_MARK: 271 case NFT_META_PRIORITY: 272 case NFT_META_NFTRACE: 273 break; 274 default: 275 return -EOPNOTSUPP; 276 } 277 278 priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG])); 279 err = nft_validate_input_register(priv->sreg); 280 if (err < 0) 281 return err; 282 283 return 0; 284 } 285 EXPORT_SYMBOL_GPL(nft_meta_set_init); 286 287 int nft_meta_get_dump(struct sk_buff *skb, 288 const struct nft_expr *expr) 289 { 290 const struct nft_meta *priv = nft_expr_priv(expr); 291 292 if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) 293 goto nla_put_failure; 294 if (nla_put_be32(skb, NFTA_META_DREG, htonl(priv->dreg))) 295 goto nla_put_failure; 296 return 0; 297 298 nla_put_failure: 299 return -1; 300 } 301 EXPORT_SYMBOL_GPL(nft_meta_get_dump); 302 303 int nft_meta_set_dump(struct sk_buff *skb, 304 const struct nft_expr *expr) 305 { 306 const struct nft_meta *priv = nft_expr_priv(expr); 307 308 if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) 309 goto nla_put_failure; 310 if (nla_put_be32(skb, NFTA_META_SREG, htonl(priv->sreg))) 311 goto nla_put_failure; 312 313 return 0; 314 315 nla_put_failure: 316 return -1; 317 } 318 EXPORT_SYMBOL_GPL(nft_meta_set_dump); 319 320 static struct nft_expr_type nft_meta_type; 321 static const struct nft_expr_ops nft_meta_get_ops = { 322 .type = &nft_meta_type, 323 .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), 324 .eval = nft_meta_get_eval, 325 .init = nft_meta_get_init, 326 .dump = nft_meta_get_dump, 327 }; 328 329 static const struct nft_expr_ops nft_meta_set_ops = { 330 .type = &nft_meta_type, 331 .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), 332 .eval = nft_meta_set_eval, 333 .init = nft_meta_set_init, 334 .dump = nft_meta_set_dump, 335 }; 336 337 static const struct nft_expr_ops * 338 nft_meta_select_ops(const struct nft_ctx *ctx, 339 const struct nlattr * const tb[]) 340 { 341 if (tb[NFTA_META_KEY] == NULL) 342 return ERR_PTR(-EINVAL); 343 344 if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) 345 return ERR_PTR(-EINVAL); 346 347 if (tb[NFTA_META_DREG]) 348 return &nft_meta_get_ops; 349 350 if (tb[NFTA_META_SREG]) 351 return &nft_meta_set_ops; 352 353 return ERR_PTR(-EINVAL); 354 } 355 356 static struct nft_expr_type nft_meta_type __read_mostly = { 357 .name = "meta", 358 .select_ops = &nft_meta_select_ops, 359 .policy = nft_meta_policy, 360 .maxattr = NFTA_META_MAX, 361 .owner = THIS_MODULE, 362 }; 363 364 static int __init nft_meta_module_init(void) 365 { 366 return nft_register_expr(&nft_meta_type); 367 } 368 369 static void __exit nft_meta_module_exit(void) 370 { 371 nft_unregister_expr(&nft_meta_type); 372 } 373 374 module_init(nft_meta_module_init); 375 module_exit(nft_meta_module_exit); 376 377 MODULE_LICENSE("GPL"); 378 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 379 MODULE_ALIAS_NFT_EXPR("meta"); 380