版权声明:如有需要,可供转载,但请注明出处:https://blog.csdn.net/City_of_skey/article/details/85254786
目录
1、netif_receive_skb
2、handle_bridge
3、br_handle_frame
4、br_handle_frame_finish
5、br_forward
6、br_flood_forward
7、br_pass_frame_up
当一个网卡收到数据包后机会触发硬件中断,然后调用网卡驱动程序生成skb并包skb拷贝到CPU输入队列,再触发软中断调用netif_receive_skb来处理数据包,netif_receive_skb主要调用了__netif_receive_skb,这个函数主要做以下四件事
(1)、遍历嗅探器ptype_all,有注册函数就拷贝一份数据包,比如tcpdump抓包就是这在ptype_all中注册了一个函数。
(2)、调用handle_bridge处理网桥
(3)、调用handle_macvlan处理vlan
(4)、根据协议号在ptype_base查找三层处理函数,如果是IP协议就调用ip_rcv数据包上三层
__netif_receive_skb:
static int __netif_receive_skb(struct sk_buff *skb)
{
struct packet_type *ptype, *pt_prev;
struct net_device *orig_dev;
struct net_device *master;
struct net_device *null_or_orig;
struct net_device *orig_or_bond;
int ret = NET_RX_DROP;
__be16 type;
if (!netdev_tstamp_prequeue)
net_timestamp_check(skb);
if (vlan_tx_tag_present(skb) && vlan_hwaccel_do_receive(skb))
return NET_RX_SUCCESS;
/* if we've gotten here through NAPI, check netpoll */
if (netpoll_receive_skb(skb))
return NET_RX_DROP;
if (!skb->skb_iif)
skb->skb_iif = skb->dev->ifindex;
/*
* bonding note: skbs received on inactive slaves should only
* be delivered to pkt handlers that are exact matches. Also
* the deliver_no_wcard flag will be set. If packet handlers
* are sensitive to duplicate packets these skbs will need to
* be dropped at the handler. The vlan accel path may have
* already set the deliver_no_wcard flag.
*/
null_or_orig = NULL;
orig_dev = skb->dev;
master = ACCESS_ONCE(orig_dev->master);
if (skb->deliver_no_wcard)
null_or_orig = orig_dev;
else if (master) {
if (skb_bond_should_drop(skb, master)) {
skb->deliver_no_wcard = 1;
null_or_orig = orig_dev; /* deliver only exact match */
} else
skb->dev = master;
}
__get_cpu_var(softnet_data).processed++;
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
skb->mac_len = skb->network_header - skb->mac_header;
pt_prev = NULL;
rcu_read_lock();
#ifdef CONFIG_NET_CLS_ACT
if (skb->tc_verd & TC_NCLS) {
skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
goto ncls;
}
#endif
/*网络嗅探器比如tcpdump抓包工具,就是
在ptype_all中注册一个函数,然后复制一份数据*/
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
ptype->dev == orig_dev) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
#ifdef CONFIG_NET_CLS_ACT
skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
if (!skb)
goto out;
ncls:
#endif
/*网桥处理函数*/
skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
if (!skb)
goto out;
/*vlan处理*/
skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
if (!skb)
goto out;
/*
* Make sure frames received on VLAN interfaces stacked on
* bonding interfaces still make their way to any base bonding
* device that may have registered for a specific ptype. The
* handler may have to adjust skb->dev and orig_dev.
*/
orig_or_bond = orig_dev;
if ((skb->dev->priv_flags & IFF_802_1Q_VLAN) &&
(vlan_dev_real_dev(skb->dev)->priv_flags & IFF_BONDING)) {
orig_or_bond = vlan_dev_real_dev(skb->dev);
}
/*根据网络协议在ptype_base中查找三层协议*/
type = skb->protocol;
list_for_each_entry_rcu(ptype,
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
if (ptype->type == type && (ptype->dev == null_or_orig ||
ptype->dev == skb->dev || ptype->dev == orig_dev ||
ptype->dev == orig_or_bond)) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
/*数据包上三层*/
if (pt_prev) {
/*三层处理函数,ip_rcv*/
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
} else {
kfree_skb(skb);
/* Jamal, now you will not able to escape explaining
* me how you were going to use this. :-)
*/
ret = NET_RX_DROP;
}
out:
rcu_read_unlock();
return ret;
}
handle_bridge首先判断数据包类型,不属于网桥设备或者是环网数据直接返回。然后调用br_handle_frame_hook进入网桥处理流程,br_handle_frame_hook在网桥初始化是赋值给函数br_handle_frame。
static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
struct packet_type **pt_prev, int *ret,
struct net_device *orig_dev)
{
struct net_bridge_port *port;
/*数据包是环网或者设备不属于任何网桥
就直接返回*/
if (skb->pkt_type == PACKET_LOOPBACK ||
(port = rcu_dereference(skb->dev->br_port)) == NULL)
return skb;
if (*pt_prev) {
*ret = deliver_skb(skb, *pt_prev, orig_dev);
*pt_prev = NULL;
}
/*网桥处理入口,网桥初始化时复制给函数br_handle_frame*/
return br_handle_frame_hook(port, skb);
}
netfileter框架在二层也维持了一个过滤系统,br_handle_frame首先判断数据包的类型是本机还是转发。
1、本地数据包首先要过二层netfilter的NF_BR_LOCAL_IN链上处理函数,最后调用br_handle_local_finish,如果数据包没有被丢弃,就返回继续上层协议处理。
2、如果是转发数据包,首先ebtables查找路由表的hook处理函数,如果找了就调用hook处理选了路由后返回继续上层协议处理。如果没有找到就进入二层netfilter框架的NF_BR_PRE_ROUTING链处理,处理完毕调用br_handle_frame_finish。
struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
int (*rhook)(struct sk_buff *skb);
/*判断mac地址是否有效,广播地址和00....00是非法地址*/
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto drop;
/*共享数据包clone一份*/
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return NULL;
/*本地数据包*/
if (unlikely(is_link_local(dest))) {
/* Pause frames shouldn't be passed up by driver anyway */
if (skb->protocol == htons(ETH_P_PAUSE))
goto drop;
/* If STP is turned off, then forward */
if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0)
goto forward;
/*二层的netfilet框架进入NF_BR_LOCAL_IN链上处理函数*/
if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
NULL, br_handle_local_finish))
return NULL; /* frame consumed by filter */
else
/*通过二层的netfilter后继续上层处理数据*/
return skb; /* continue processing */
}
/*转发处理*/
forward:
switch (p->state) {
case BR_STATE_FORWARDING:
/*ebtables查找路由的hook点*/
rhook = rcu_dereference(br_should_route_hook);
if (rhook != NULL) {
/*如果找到了就处理,然后继续数据包上层处理*/
if (rhook(skb))
return skb;
dest = eth_hdr(skb)->h_dest;
}
/* fall through */
case BR_STATE_LEARNING:
if (!compare_ether_addr(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
/*NF_BR_PRE_ROUTING表处理完毕调用br_handle_frame_finish*/
NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
break;
default:
drop:
kfree_skb(skb);
}
return NULL;
}
br_handle_frame_finish是经过netfilter的NF_BR_PRE_ROUTING链处理后调用,主要做以下事情:
(1)判断桥端口是否是开启的,如果没有开启就直接返回
(2)调用br_fdb_update做mac地址学习
(3)判断目的地址类型如果是多播地址就调用br_multicast_forward转发
(4)根据目的地址查找端口的转发表,如果找到了就调用br_forward从此端口转发出去,如果没有找到对应的端口就调用br_flood_forward每个端口转发一份
/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct sk_buff *skb)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
struct net_bridge *br;
struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst;
struct sk_buff *skb2;
/*桥端口是否是开启状况*/
if (!p || p->state == BR_STATE_DISABLED)
goto drop;
/* insert into forwarding database after filtering to avoid spoofing */
br = p->br;
/*mac地址学习,更新端口-MAC表*/
br_fdb_update(br, p, eth_hdr(skb)->h_source);
if (is_multicast_ether_addr(dest) &&
br_multicast_rcv(br, p, skb))
goto drop;
if (p->state == BR_STATE_LEARNING)
goto drop;
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
/* The packet skb2 goes to the local host (NULL to skip). */
skb2 = NULL;
if (br->dev->flags & IFF_PROMISC)
skb2 = skb;
dst = NULL;
/*目的地址是一个多播地址*/
if (is_multicast_ether_addr(dest)) {
mdst = br_mdb_get(br, skb);
if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {
if ((mdst && !hlist_unhashed(&mdst->mglist)) ||
br_multicast_is_router(br))
skb2 = skb;
/*多播转发处理*/
br_multicast_forward(mdst, skb, skb2);
skb = NULL;
if (!skb2)
goto out;
} else
skb2 = skb;
br->dev->stats.multicast++;
/*根据mac地址获取对应的端口-MAC表*/
} else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) {
skb2 = skb;
/* Do not forward the packet since it's local. */
skb = NULL;
}
if (skb) {
if (dst)
/*找到了mac对应的端口就转发*/
br_forward(dst->dst, skb, skb2);
else
/*转发表中没有找到该mac对应的端口就每个端口转发一份*/
br_flood_forward(br, skb, skb2);
}
/*副本数据发完本地,走三层转发*/
if (skb2)
return br_pass_frame_up(skb2);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
网桥转发处理走br_forward,最终处理的是__br_forward函数
/* called with rcu_read_lock */
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0)
{
if (should_deliver(to, skb)) {
if (skb0)
deliver_clone(to, skb, __br_forward);
else
__br_forward(to, skb);
return;
}
if (!skb0)
kfree_skb(skb);
}
__br_forward函数数据包先通过二层netfilter框架的NF_BR_FORWARD链处理再调用br_forward_finish函数继续。
__br_forward:
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{
struct net_device *indev;
if (skb_warn_if_lro(skb)) {
kfree_skb(skb);
return;
}
indev = skb->dev;
skb->dev = to->dev;
skb_forward_csum(skb);
NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
br_forward_finish);
}
br_forward_finish函数要通过NF_BR_LOCAL_OUT链的过滤处理在调用br_dev_queue_push_xmit继续处理。
br_forward_finish:
int br_forward_finish(struct sk_buff *skb)
{
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
br_dev_queue_push_xmit);
}
br_dev_queue_push_xmit函数最终调用dev_queue_xmit通过网卡驱动将数据包发送出去。
br_dev_queue_push_xmit:
int br_dev_queue_push_xmit(struct sk_buff *skb)
{
/* drop mtu oversized packets except gso */
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
kfree_skb(skb);
else {
/* ip_fragment doesn't copy the MAC header */
if (nf_bridge_maybe_copy_header(skb))
kfree_skb(skb);
else {
skb_push(skb, ETH_HLEN);
dev_queue_xmit(skb); //驱动转发出去
}
}
return 0;
}
br_flood_forward处理方法是遍历网桥下面的所有端口,每个端口调用__br_forward发送一次。
/* called under bridge lock */
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
struct sk_buff *skb2)
{
br_flood(br, skb, skb2, __br_forward);
}
br_flood:
/* called under bridge lock */
static void br_flood(struct net_bridge *br, struct sk_buff *skb,
struct sk_buff *skb0,
void (*__packet_hook)(const struct net_bridge_port *p,
struct sk_buff *skb))
{
struct net_bridge_port *p;
struct net_bridge_port *prev;
prev = NULL;
/*遍历网桥下端口链表port_list每个端口调用__br_forward发送一次*/
list_for_each_entry_rcu(p, &br->port_list, list) {
prev = maybe_deliver(prev, p, skb, __packet_hook);
if (IS_ERR(prev))
goto out;
}
if (!prev)
goto out;
if (skb0)
deliver_clone(prev, skb, __packet_hook);
else
__packet_hook(prev, skb);
return;
out:
if (!skb0)
kfree_skb(skb);
}
br_pass_frame_up会复制一份副本再次进入netif_receive_skb函数走三层转发处理,不过这次不会在进入handle_bridge,因为skb->dev是网桥设备而不是一个端口设备。
static int br_pass_frame_up(struct sk_buff *skb)
{
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
struct net_bridge *br = netdev_priv(brdev);
struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
/*统计网桥收包数*/
brstats->rx_packets++;
brstats->rx_bytes += skb->len;
/*将数据包的设备改成网桥的设备*/
indev = skb->dev;
skb->dev = brdev;
/*通过NF_BR_LOCAL_IN链处理再次进入netif_receive_skb函数*/
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
netif_receive_skb);
}