linux netfilter包括层2 bridge的netfilter和层3 ip netfilter
先看bridge的netfilter的brouting流程
brouting用于控制进来的数据包是需要进行bridge转发还是进行route转发,即2层转发和3层转发。
BROUTING 过滤配置为ACCEPT表示走bridge流程,DROP表示走route 流程。
br_handle_frame
\net\bridge
br_input.c
br_handle_frame 是底层网卡驱动收到数据后开始传递给br的入口,处理以太网链路层数据帧。
/*
* Return NULL if skb is handled
* note: already called with rcu_read_lock
*/
rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{
switch (p->state) {
case BR_STATE_FORWARDING:
#def 如果注册了br_should_route_hook 调用hook,br_should_route_hook 在ebtable_broute.c中ebtable_broute_init函数设置为ebt_broute
rhook = rcu_dereference(br_should_route_hook);
if (rhook) {
#def 如果返回0,函数继续走BR_STATE_LEARNING阶段,也就是继续bridge的流程
#def 如果返回非0,函数直接返回RX_HANDLER_PASS,不再走bridge,而是返回走ip 层路由
if ((*rhook)(skb)) {
*pskb = skb;
return RX_HANDLER_PASS;
}
dest = eth_hdr(skb)->h_dest;
}
/* fall through */
case BR_STATE_LEARNING:
if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
break;
default:
}
enum rx_handler_result {
RX_HANDLER_CONSUMED,
RX_HANDLER_ANOTHER,
RX_HANDLER_EXACT,
RX_HANDLER_PASS,
};
先看看返回RX_HANDLER_PASS时处理,
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
.....
rx_handler = rcu_dereference(skb->dev->rx_handler);
if (rx_handler) {
if (pt_prev) {
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = NULL;
}
switch (rx_handler(&skb)) {
#def RX_HANDLER_CONSUMED表示bridge处理完成,继续释放返回
case RX_HANDLER_CONSUMED:
ret = NET_RX_SUCCESS;
goto unlock;
case RX_HANDLER_ANOTHER:
goto another_round;
case RX_HANDLER_EXACT:
deliver_exact = true;
#def RX_HANDLER_PASS表示bridge 不再处理,继续走ip流程
case RX_HANDLER_PASS:
break;
default:
BUG();
}
}
if (unlikely(vlan_tx_tag_present(skb))) {
if (vlan_tx_tag_get_id(skb))
skb->pkt_type = PACKET_OTHERHOST;
/* Note: we might in the future use prio bits
* and set skb->priority like in vlan_do_receive()
* For the time being, just ignore Priority Code Point
*/
skb->vlan_tci = 0;
}
/* deliver only exact match when indicated */
null_or_dev = deliver_exact ? skb->dev : NULL;
type = skb->protocol;
#def 找ip协议处理函数
list_for_each_entry_rcu(ptype,
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
if (ptype->type == type &&
(ptype->dev == null_or_dev || ptype->dev == skb->dev ||
ptype->dev == orig_dev)) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
#def 走ip处理流程
if (pt_prev) {
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
goto drop;
else
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
....
}
ebt_broute
回来继续看br_should_route_hook流程
static int ebt_broute(struct sk_buff *skb)
{
int ret;
#def 过滤配置在NF_BR_BROUTING 阶段,使用broute_table 表
ret = ebt_do_table(NF_BR_BROUTING, skb, skb->dev, NULL,
dev_net(skb->dev)->xt.broute_table);
#def 过滤配置返回NF_DROP 不需要后续bridge流程,去ip层走route流程
if (ret == NF_DROP)
return 1; /* route it */
#def 过滤配置accept,继续bridge流程
return 0; /* bridge it */
}
/* Do some firewalling */
unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
struct ebt_table *table)
{
int i, nentries;
struct ebt_entry *point;
struct ebt_counter *counter_base, *cb_base;
const struct ebt_entry_target *t;
int verdict, sp = 0;
struct ebt_chainstack *cs;
struct ebt_entries *chaininfo;
const char *base;
const struct ebt_table_info *private;
struct xt_action_param acpar;
#def 把匹配的选项存储在acpar中,包括hook点NF_BR_BROUTING,skb的输入网卡dev
acpar.family = NFPROTO_BRIDGE;
acpar.in = in;
acpar.out = out;
acpar.hotdrop = false;
acpar.hooknum = hook;
read_lock_bh(&table->lock);
#def private中存储了过滤配置ebt_table_info ,各种规则,动作
private = table->private;
cb_base = COUNTER_BASE(private->counters, private->nentries,
smp_processor_id());
if (private->chainstack)
cs = private->chainstack[smp_processor_id()];
else
cs = NULL;
#def 过滤配置 hook_entry[hook]->data执行了hook点匹配规则链表的头指针
chaininfo = private->hook_entry[hook];
nentries = private->hook_entry[hook]->nentries;
point = (struct ebt_entry *)(private->hook_entry[hook]->data);
counter_base = cb_base + private->hook_entry[hook]->counter_offset;
/* base for chain jumps */
base = private->entries;
i = 0;
#def 过滤配置检查
while (i < nentries) {
#def 基本规则匹配
if (ebt_basic_match(point, skb, in, out))
goto letscontinue;
if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &acpar) != 0)
goto letscontinue;
if (acpar.hotdrop) {
read_unlock_bh(&table->lock);
return NF_DROP;
}
/* increase counter */
(*(counter_base + i)).pcnt++;
(*(counter_base + i)).bcnt += skb->len;
/* these should only watch: not modify, nor tell us
what to do with the packet */
EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar);
t = (struct ebt_entry_target *)
(((char *)point) + point->target_offset);
/* standard target */
if (!t->u.target->target)
verdict = ((struct ebt_standard_target *)t)->verdict;
else {
acpar.target = t->u.target;
acpar.targinfo = t->data;
verdict = t->u.target->target(skb, &acpar);
}
if (verdict == EBT_ACCEPT) {
read_unlock_bh(&table->lock);
return NF_ACCEPT;
}
if (verdict == EBT_DROP) {
read_unlock_bh(&table->lock);
return NF_DROP;
}
if (verdict == EBT_RETURN) {
letsreturn:
#ifdef CONFIG_NETFILTER_DEBUG
if (sp == 0) {
BUGPRINT("RETURN on base chain");
/* act like this is EBT_CONTINUE */
goto letscontinue;
}
#endif
sp--;
/* put all the local variables right */
i = cs[sp].n;
chaininfo = cs[sp].chaininfo;
nentries = chaininfo->nentries;
point = cs[sp].e;
counter_base = cb_base +
chaininfo->counter_offset;
continue;
}
if (verdict == EBT_CONTINUE)
goto letscontinue;
#ifdef CONFIG_NETFILTER_DEBUG
if (verdict < 0) {
BUGPRINT("bogus standard verdict\n");
read_unlock_bh(&table->lock);
return NF_DROP;
}
#endif
/* jump to a udc */
cs[sp].n = i + 1;
cs[sp].chaininfo = chaininfo;
cs[sp].e = ebt_next_entry(point);
i = 0;
chaininfo = (struct ebt_entries *) (base + verdict);
#ifdef CONFIG_NETFILTER_DEBUG
if (chaininfo->distinguisher) {
BUGPRINT("jump to non-chain\n");
read_unlock_bh(&table->lock);
return NF_DROP;
}
#endif
nentries = chaininfo->nentries;
point = (struct ebt_entry *)chaininfo->data;
counter_base = cb_base + chaininfo->counter_offset;
sp++;
continue;
letscontinue:
point = ebt_next_entry(point);
i++;
}
/* I actually like this :) */
if (chaininfo->policy == EBT_RETURN)
goto letsreturn;
if (chaininfo->policy == EBT_ACCEPT) {
read_unlock_bh(&table->lock);
return NF_ACCEPT;
}
read_unlock_bh(&table->lock);
return NF_DROP;
}