1 /* 2 * Copyright (c) 2008 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/module.h> 13 #include <linux/init.h> 14 #include <linux/list.h> 15 #include <linux/rculist.h> 16 #include <linux/skbuff.h> 17 #include <linux/netlink.h> 18 #include <linux/netfilter.h> 19 #include <linux/netfilter/nfnetlink.h> 20 #include <linux/netfilter/nf_tables.h> 21 #include <net/netfilter/nf_tables_core.h> 22 #include <net/netfilter/nf_tables.h> 23 #include <net/netfilter/nf_log.h> 24 25 enum nft_trace { 26 NFT_TRACE_RULE, 27 NFT_TRACE_RETURN, 28 NFT_TRACE_POLICY, 29 }; 30 31 static const char *const comments[] = { 32 [NFT_TRACE_RULE] = "rule", 33 [NFT_TRACE_RETURN] = "return", 34 [NFT_TRACE_POLICY] = "policy", 35 }; 36 37 static struct nf_loginfo trace_loginfo = { 38 .type = NF_LOG_TYPE_LOG, 39 .u = { 40 .log = { 41 .level = LOGLEVEL_WARNING, 42 .logflags = NF_LOG_MASK, 43 }, 44 }, 45 }; 46 47 static void __nft_trace_packet(const struct nft_pktinfo *pkt, 48 const struct nft_chain *chain, 49 int rulenum, enum nft_trace type) 50 { 51 struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); 52 53 nf_log_trace(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in, 54 pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", 55 chain->table->name, chain->name, comments[type], 56 rulenum); 57 } 58 59 static inline void nft_trace_packet(const struct nft_pktinfo *pkt, 60 const struct nft_chain *chain, 61 int rulenum, enum nft_trace type) 62 { 63 if (unlikely(pkt->skb->nf_trace)) 64 __nft_trace_packet(pkt, chain, rulenum, type); 65 } 66 67 static void nft_cmp_fast_eval(const struct nft_expr *expr, 68 struct nft_regs *regs) 69 { 70 const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 71 u32 mask = nft_cmp_fast_mask(priv->len); 72 73 if ((regs->data[priv->sreg] & mask) == priv->data) 74 return; 75 regs->verdict.code = NFT_BREAK; 76 } 77 78 static bool nft_payload_fast_eval(const struct nft_expr *expr, 79 struct nft_regs *regs, 80 const struct nft_pktinfo *pkt) 81 { 82 const struct nft_payload *priv = nft_expr_priv(expr); 83 const struct sk_buff *skb = pkt->skb; 84 u32 *dest = ®s->data[priv->dreg]; 85 unsigned char *ptr; 86 87 if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) 88 ptr = skb_network_header(skb); 89 else 90 ptr = skb_network_header(skb) + pkt->xt.thoff; 91 92 ptr += priv->offset; 93 94 if (unlikely(ptr + priv->len >= skb_tail_pointer(skb))) 95 return false; 96 97 *dest = 0; 98 if (priv->len == 2) 99 *(u16 *)dest = *(u16 *)ptr; 100 else if (priv->len == 4) 101 *(u32 *)dest = *(u32 *)ptr; 102 else 103 *(u8 *)dest = *(u8 *)ptr; 104 return true; 105 } 106 107 struct nft_jumpstack { 108 const struct nft_chain *chain; 109 const struct nft_rule *rule; 110 int rulenum; 111 }; 112 113 unsigned int 114 nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) 115 { 116 const struct nft_chain *chain = ops->priv, *basechain = chain; 117 const struct net *chain_net = read_pnet(&nft_base_chain(basechain)->pnet); 118 const struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); 119 const struct nft_rule *rule; 120 const struct nft_expr *expr, *last; 121 struct nft_regs regs; 122 unsigned int stackptr = 0; 123 struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; 124 struct nft_stats *stats; 125 int rulenum; 126 unsigned int gencursor = nft_genmask_cur(net); 127 128 /* Ignore chains that are not for the current network namespace */ 129 if (!net_eq(net, chain_net)) 130 return NF_ACCEPT; 131 132 do_chain: 133 rulenum = 0; 134 rule = list_entry(&chain->rules, struct nft_rule, list); 135 next_rule: 136 regs.verdict.code = NFT_CONTINUE; 137 list_for_each_entry_continue_rcu(rule, &chain->rules, list) { 138 139 /* This rule is not active, skip. */ 140 if (unlikely(rule->genmask & (1 << gencursor))) 141 continue; 142 143 rulenum++; 144 145 nft_rule_for_each_expr(expr, last, rule) { 146 if (expr->ops == &nft_cmp_fast_ops) 147 nft_cmp_fast_eval(expr, ®s); 148 else if (expr->ops != &nft_payload_fast_ops || 149 !nft_payload_fast_eval(expr, ®s, pkt)) 150 expr->ops->eval(expr, ®s, pkt); 151 152 if (regs.verdict.code != NFT_CONTINUE) 153 break; 154 } 155 156 switch (regs.verdict.code) { 157 case NFT_BREAK: 158 regs.verdict.code = NFT_CONTINUE; 159 continue; 160 case NFT_CONTINUE: 161 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); 162 continue; 163 } 164 break; 165 } 166 167 switch (regs.verdict.code & NF_VERDICT_MASK) { 168 case NF_ACCEPT: 169 case NF_DROP: 170 case NF_QUEUE: 171 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); 172 return regs.verdict.code; 173 } 174 175 switch (regs.verdict.code) { 176 case NFT_JUMP: 177 BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); 178 jumpstack[stackptr].chain = chain; 179 jumpstack[stackptr].rule = rule; 180 jumpstack[stackptr].rulenum = rulenum; 181 stackptr++; 182 /* fall through */ 183 case NFT_GOTO: 184 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); 185 186 chain = regs.verdict.chain; 187 goto do_chain; 188 case NFT_CONTINUE: 189 rulenum++; 190 /* fall through */ 191 case NFT_RETURN: 192 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); 193 break; 194 default: 195 WARN_ON(1); 196 } 197 198 if (stackptr > 0) { 199 stackptr--; 200 chain = jumpstack[stackptr].chain; 201 rule = jumpstack[stackptr].rule; 202 rulenum = jumpstack[stackptr].rulenum; 203 goto next_rule; 204 } 205 206 nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY); 207 208 rcu_read_lock_bh(); 209 stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats)); 210 u64_stats_update_begin(&stats->syncp); 211 stats->pkts++; 212 stats->bytes += pkt->skb->len; 213 u64_stats_update_end(&stats->syncp); 214 rcu_read_unlock_bh(); 215 216 return nft_base_chain(basechain)->policy; 217 } 218 EXPORT_SYMBOL_GPL(nft_do_chain); 219 220 int __init nf_tables_core_module_init(void) 221 { 222 int err; 223 224 err = nft_immediate_module_init(); 225 if (err < 0) 226 goto err1; 227 228 err = nft_cmp_module_init(); 229 if (err < 0) 230 goto err2; 231 232 err = nft_lookup_module_init(); 233 if (err < 0) 234 goto err3; 235 236 err = nft_bitwise_module_init(); 237 if (err < 0) 238 goto err4; 239 240 err = nft_byteorder_module_init(); 241 if (err < 0) 242 goto err5; 243 244 err = nft_payload_module_init(); 245 if (err < 0) 246 goto err6; 247 248 err = nft_dynset_module_init(); 249 if (err < 0) 250 goto err7; 251 252 return 0; 253 254 err7: 255 nft_payload_module_exit(); 256 err6: 257 nft_byteorder_module_exit(); 258 err5: 259 nft_bitwise_module_exit(); 260 err4: 261 nft_lookup_module_exit(); 262 err3: 263 nft_cmp_module_exit(); 264 err2: 265 nft_immediate_module_exit(); 266 err1: 267 return err; 268 } 269 270 void nf_tables_core_module_exit(void) 271 { 272 nft_dynset_module_exit(); 273 nft_payload_module_exit(); 274 nft_byteorder_module_exit(); 275 nft_bitwise_module_exit(); 276 nft_lookup_module_exit(); 277 nft_cmp_module_exit(); 278 nft_immediate_module_exit(); 279 } 280