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