当前位置: 代码迷 >> 综合 >> linux netfilter--broute流程
  详细解决方案

linux netfilter--broute流程

热度:96   发布时间:2024-02-05 08:54:09.0

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_brouterhook = 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;elseret = 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;acpar.family  = NFPROTO_BRIDGE;acpar.in      = in;acpar.out     = out;acpar.hotdrop = false;acpar.hooknum = hook;read_lock_bh(&table->lock);#def private中存储了过滤配置选项private = table->private;cb_base = COUNTER_BASE(private->counters, private->nentries,smp_processor_id());if (private->chainstack)cs = private->chainstack[smp_processor_id()];elsecs = NULL;#def 过滤配置lianchaininfo = 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) {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 uswhat 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_DEBUGif (sp == 0) {BUGPRINT("RETURN on base chain");/* act like this is EBT_CONTINUE */goto letscontinue;}
#endifsp--;/* 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_DEBUGif (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_DEBUGif (chaininfo->distinguisher) {BUGPRINT("jump to non-chain\n");read_unlock_bh(&table->lock);return NF_DROP;}
#endifnentries = 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;
}