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 <linux/static_key.h> 22 #include <net/dst.h> 23 #include <net/sock.h> 24 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */ 25 #include <net/netfilter/nf_tables.h> 26 #include <net/netfilter/nf_tables_core.h> 27 #include <net/netfilter/nft_meta.h> 28 29 #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */ 30 31 void nft_meta_get_eval(const struct nft_expr *expr, 32 struct nft_regs *regs, 33 const struct nft_pktinfo *pkt) 34 { 35 const struct nft_meta *priv = nft_expr_priv(expr); 36 const struct sk_buff *skb = pkt->skb; 37 const struct net_device *in = pkt->in, *out = pkt->out; 38 struct sock *sk; 39 u32 *dest = ®s->data[priv->dreg]; 40 41 switch (priv->key) { 42 case NFT_META_LEN: 43 *dest = skb->len; 44 break; 45 case NFT_META_PROTOCOL: 46 *dest = 0; 47 *(__be16 *)dest = skb->protocol; 48 break; 49 case NFT_META_NFPROTO: 50 *dest = pkt->pf; 51 break; 52 case NFT_META_L4PROTO: 53 *dest = pkt->tprot; 54 break; 55 case NFT_META_PRIORITY: 56 *dest = skb->priority; 57 break; 58 case NFT_META_MARK: 59 *dest = skb->mark; 60 break; 61 case NFT_META_IIF: 62 if (in == NULL) 63 goto err; 64 *dest = in->ifindex; 65 break; 66 case NFT_META_OIF: 67 if (out == NULL) 68 goto err; 69 *dest = out->ifindex; 70 break; 71 case NFT_META_IIFNAME: 72 if (in == NULL) 73 goto err; 74 strncpy((char *)dest, in->name, IFNAMSIZ); 75 break; 76 case NFT_META_OIFNAME: 77 if (out == NULL) 78 goto err; 79 strncpy((char *)dest, out->name, IFNAMSIZ); 80 break; 81 case NFT_META_IIFTYPE: 82 if (in == NULL) 83 goto err; 84 *dest = 0; 85 *(u16 *)dest = in->type; 86 break; 87 case NFT_META_OIFTYPE: 88 if (out == NULL) 89 goto err; 90 *dest = 0; 91 *(u16 *)dest = out->type; 92 break; 93 case NFT_META_SKUID: 94 sk = skb_to_full_sk(skb); 95 if (!sk || !sk_fullsock(sk)) 96 goto err; 97 98 read_lock_bh(&sk->sk_callback_lock); 99 if (sk->sk_socket == NULL || 100 sk->sk_socket->file == NULL) { 101 read_unlock_bh(&sk->sk_callback_lock); 102 goto err; 103 } 104 105 *dest = from_kuid_munged(&init_user_ns, 106 sk->sk_socket->file->f_cred->fsuid); 107 read_unlock_bh(&sk->sk_callback_lock); 108 break; 109 case NFT_META_SKGID: 110 sk = skb_to_full_sk(skb); 111 if (!sk || !sk_fullsock(sk)) 112 goto err; 113 114 read_lock_bh(&sk->sk_callback_lock); 115 if (sk->sk_socket == NULL || 116 sk->sk_socket->file == NULL) { 117 read_unlock_bh(&sk->sk_callback_lock); 118 goto err; 119 } 120 *dest = from_kgid_munged(&init_user_ns, 121 sk->sk_socket->file->f_cred->fsgid); 122 read_unlock_bh(&sk->sk_callback_lock); 123 break; 124 #ifdef CONFIG_IP_ROUTE_CLASSID 125 case NFT_META_RTCLASSID: { 126 const struct dst_entry *dst = skb_dst(skb); 127 128 if (dst == NULL) 129 goto err; 130 *dest = dst->tclassid; 131 break; 132 } 133 #endif 134 #ifdef CONFIG_NETWORK_SECMARK 135 case NFT_META_SECMARK: 136 *dest = skb->secmark; 137 break; 138 #endif 139 case NFT_META_PKTTYPE: 140 if (skb->pkt_type != PACKET_LOOPBACK) { 141 *dest = skb->pkt_type; 142 break; 143 } 144 145 switch (pkt->pf) { 146 case NFPROTO_IPV4: 147 if (ipv4_is_multicast(ip_hdr(skb)->daddr)) 148 *dest = PACKET_MULTICAST; 149 else 150 *dest = PACKET_BROADCAST; 151 break; 152 case NFPROTO_IPV6: 153 if (ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF) 154 *dest = PACKET_MULTICAST; 155 else 156 *dest = PACKET_BROADCAST; 157 break; 158 default: 159 WARN_ON(1); 160 goto err; 161 } 162 break; 163 case NFT_META_CPU: 164 *dest = raw_smp_processor_id(); 165 break; 166 case NFT_META_IIFGROUP: 167 if (in == NULL) 168 goto err; 169 *dest = in->group; 170 break; 171 case NFT_META_OIFGROUP: 172 if (out == NULL) 173 goto err; 174 *dest = out->group; 175 break; 176 #ifdef CONFIG_CGROUP_NET_CLASSID 177 case NFT_META_CGROUP: 178 sk = skb_to_full_sk(skb); 179 if (!sk || !sk_fullsock(sk)) 180 goto err; 181 *dest = sock_cgroup_classid(&sk->sk_cgrp_data); 182 break; 183 #endif 184 default: 185 WARN_ON(1); 186 goto err; 187 } 188 return; 189 190 err: 191 regs->verdict.code = NFT_BREAK; 192 } 193 EXPORT_SYMBOL_GPL(nft_meta_get_eval); 194 195 /* don't change or set _LOOPBACK, _USER, etc. */ 196 static bool pkt_type_ok(u32 p) 197 { 198 return p == PACKET_HOST || p == PACKET_BROADCAST || 199 p == PACKET_MULTICAST || p == PACKET_OTHERHOST; 200 } 201 202 void nft_meta_set_eval(const struct nft_expr *expr, 203 struct nft_regs *regs, 204 const struct nft_pktinfo *pkt) 205 { 206 const struct nft_meta *meta = nft_expr_priv(expr); 207 struct sk_buff *skb = pkt->skb; 208 u32 value = regs->data[meta->sreg]; 209 210 switch (meta->key) { 211 case NFT_META_MARK: 212 skb->mark = value; 213 break; 214 case NFT_META_PRIORITY: 215 skb->priority = value; 216 break; 217 case NFT_META_PKTTYPE: 218 if (skb->pkt_type != value && 219 pkt_type_ok(value) && pkt_type_ok(skb->pkt_type)) 220 skb->pkt_type = value; 221 break; 222 case NFT_META_NFTRACE: 223 skb->nf_trace = 1; 224 break; 225 default: 226 WARN_ON(1); 227 } 228 } 229 EXPORT_SYMBOL_GPL(nft_meta_set_eval); 230 231 const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { 232 [NFTA_META_DREG] = { .type = NLA_U32 }, 233 [NFTA_META_KEY] = { .type = NLA_U32 }, 234 [NFTA_META_SREG] = { .type = NLA_U32 }, 235 }; 236 EXPORT_SYMBOL_GPL(nft_meta_policy); 237 238 int nft_meta_get_init(const struct nft_ctx *ctx, 239 const struct nft_expr *expr, 240 const struct nlattr * const tb[]) 241 { 242 struct nft_meta *priv = nft_expr_priv(expr); 243 unsigned int len; 244 245 priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); 246 switch (priv->key) { 247 case NFT_META_PROTOCOL: 248 case NFT_META_IIFTYPE: 249 case NFT_META_OIFTYPE: 250 len = sizeof(u16); 251 break; 252 case NFT_META_NFPROTO: 253 case NFT_META_L4PROTO: 254 case NFT_META_LEN: 255 case NFT_META_PRIORITY: 256 case NFT_META_MARK: 257 case NFT_META_IIF: 258 case NFT_META_OIF: 259 case NFT_META_SKUID: 260 case NFT_META_SKGID: 261 #ifdef CONFIG_IP_ROUTE_CLASSID 262 case NFT_META_RTCLASSID: 263 #endif 264 #ifdef CONFIG_NETWORK_SECMARK 265 case NFT_META_SECMARK: 266 #endif 267 case NFT_META_PKTTYPE: 268 case NFT_META_CPU: 269 case NFT_META_IIFGROUP: 270 case NFT_META_OIFGROUP: 271 #ifdef CONFIG_CGROUP_NET_CLASSID 272 case NFT_META_CGROUP: 273 #endif 274 len = sizeof(u32); 275 break; 276 case NFT_META_IIFNAME: 277 case NFT_META_OIFNAME: 278 len = IFNAMSIZ; 279 break; 280 default: 281 return -EOPNOTSUPP; 282 } 283 284 priv->dreg = nft_parse_register(tb[NFTA_META_DREG]); 285 return nft_validate_register_store(ctx, priv->dreg, NULL, 286 NFT_DATA_VALUE, len); 287 } 288 EXPORT_SYMBOL_GPL(nft_meta_get_init); 289 290 static int nft_meta_set_init_pkttype(const struct nft_ctx *ctx) 291 { 292 unsigned int hooks; 293 294 switch (ctx->afi->family) { 295 case NFPROTO_BRIDGE: 296 hooks = 1 << NF_BR_PRE_ROUTING; 297 break; 298 case NFPROTO_NETDEV: 299 hooks = 1 << NF_NETDEV_INGRESS; 300 break; 301 default: 302 return -EOPNOTSUPP; 303 } 304 305 return nft_chain_validate_hooks(ctx->chain, hooks); 306 } 307 308 int nft_meta_set_init(const struct nft_ctx *ctx, 309 const struct nft_expr *expr, 310 const struct nlattr * const tb[]) 311 { 312 struct nft_meta *priv = nft_expr_priv(expr); 313 unsigned int len; 314 int err; 315 316 priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); 317 switch (priv->key) { 318 case NFT_META_MARK: 319 case NFT_META_PRIORITY: 320 len = sizeof(u32); 321 break; 322 case NFT_META_NFTRACE: 323 len = sizeof(u8); 324 break; 325 case NFT_META_PKTTYPE: 326 err = nft_meta_set_init_pkttype(ctx); 327 if (err) 328 return err; 329 len = sizeof(u8); 330 break; 331 default: 332 return -EOPNOTSUPP; 333 } 334 335 priv->sreg = nft_parse_register(tb[NFTA_META_SREG]); 336 err = nft_validate_register_load(priv->sreg, len); 337 if (err < 0) 338 return err; 339 340 if (priv->key == NFT_META_NFTRACE) 341 static_branch_inc(&nft_trace_enabled); 342 343 return 0; 344 } 345 EXPORT_SYMBOL_GPL(nft_meta_set_init); 346 347 int nft_meta_get_dump(struct sk_buff *skb, 348 const struct nft_expr *expr) 349 { 350 const struct nft_meta *priv = nft_expr_priv(expr); 351 352 if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) 353 goto nla_put_failure; 354 if (nft_dump_register(skb, NFTA_META_DREG, priv->dreg)) 355 goto nla_put_failure; 356 return 0; 357 358 nla_put_failure: 359 return -1; 360 } 361 EXPORT_SYMBOL_GPL(nft_meta_get_dump); 362 363 int nft_meta_set_dump(struct sk_buff *skb, 364 const struct nft_expr *expr) 365 { 366 const struct nft_meta *priv = nft_expr_priv(expr); 367 368 if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) 369 goto nla_put_failure; 370 if (nft_dump_register(skb, NFTA_META_SREG, priv->sreg)) 371 goto nla_put_failure; 372 373 return 0; 374 375 nla_put_failure: 376 return -1; 377 } 378 EXPORT_SYMBOL_GPL(nft_meta_set_dump); 379 380 void nft_meta_set_destroy(const struct nft_ctx *ctx, 381 const struct nft_expr *expr) 382 { 383 const struct nft_meta *priv = nft_expr_priv(expr); 384 385 if (priv->key == NFT_META_NFTRACE) 386 static_branch_dec(&nft_trace_enabled); 387 } 388 EXPORT_SYMBOL_GPL(nft_meta_set_destroy); 389 390 static struct nft_expr_type nft_meta_type; 391 static const struct nft_expr_ops nft_meta_get_ops = { 392 .type = &nft_meta_type, 393 .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), 394 .eval = nft_meta_get_eval, 395 .init = nft_meta_get_init, 396 .dump = nft_meta_get_dump, 397 }; 398 399 static const struct nft_expr_ops nft_meta_set_ops = { 400 .type = &nft_meta_type, 401 .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), 402 .eval = nft_meta_set_eval, 403 .init = nft_meta_set_init, 404 .destroy = nft_meta_set_destroy, 405 .dump = nft_meta_set_dump, 406 }; 407 408 static const struct nft_expr_ops * 409 nft_meta_select_ops(const struct nft_ctx *ctx, 410 const struct nlattr * const tb[]) 411 { 412 if (tb[NFTA_META_KEY] == NULL) 413 return ERR_PTR(-EINVAL); 414 415 if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) 416 return ERR_PTR(-EINVAL); 417 418 if (tb[NFTA_META_DREG]) 419 return &nft_meta_get_ops; 420 421 if (tb[NFTA_META_SREG]) 422 return &nft_meta_set_ops; 423 424 return ERR_PTR(-EINVAL); 425 } 426 427 static struct nft_expr_type nft_meta_type __read_mostly = { 428 .name = "meta", 429 .select_ops = &nft_meta_select_ops, 430 .policy = nft_meta_policy, 431 .maxattr = NFTA_META_MAX, 432 .owner = THIS_MODULE, 433 }; 434 435 static int __init nft_meta_module_init(void) 436 { 437 return nft_register_expr(&nft_meta_type); 438 } 439 440 static void __exit nft_meta_module_exit(void) 441 { 442 nft_unregister_expr(&nft_meta_type); 443 } 444 445 module_init(nft_meta_module_init); 446 module_exit(nft_meta_module_exit); 447 448 MODULE_LICENSE("GPL"); 449 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 450 MODULE_ALIAS_NFT_EXPR("meta"); 451