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