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