1.vxlan口收包处理流程

前面几章详细介绍了vxlan接口的使用,而且已经知道vxlan是 MAC IN UDP中的封装,因此,在解封装之前,一切按照原有流程走,在此复习一下内核收发包流程(驱动层的数据处理这次不再解析,直接从__netif_receive_skb_core开始)

#物理网卡处理中断,触发softirq
i40e_intr└─napi_schedule_irqoff#i40e softirq把包放到per cpu backlog,触发softirq
i40e_napi_poll└─i40e_clean_rx_irq├─i40e_fetch_rx_buffer|   └─__napi_alloc_skb|       └─skb->dev = napi->dev;├─i40e_process_skb_fields└─i40e_receive_skb└─napi_gro_receive└─napi_skb_finish└─netif_receive_skb_internal└─__netif_receive_skb

__netif_receive_skb
---__netif_receive_skb_one_core
------__netif_receive_skb_corestatic int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,struct packet_type **ppt_prev)
{struct packet_type *ptype, *pt_prev;rx_handler_func_t *rx_handler;......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)) {case RX_HANDLER_CONSUMED:ret = NET_RX_SUCCESS;goto out;case RX_HANDLER_ANOTHER:goto another_round;case RX_HANDLER_EXACT:deliver_exact = true;case RX_HANDLER_PASS:break;default:BUG();}}.......type = skb->protocol;/* deliver only exact match when indicated */if (likely(!deliver_exact)) {deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,&ptype_base[ntohs(type) &PTYPE_HASH_MASK]);    //根据IP协议注册函数,回调ip_rcv}deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,&orig_dev->ptype_specific);if (unlikely(skb->dev != orig_dev)) {deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,&skb->dev->ptype_specific);}........}
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,struct net_device *orig_dev)
{struct net *net = dev_net(dev);skb = ip_rcv_core(skb, net);if (skb == NULL)return NET_RX_DROP;return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,net, NULL, skb, dev, NULL,ip_rcv_finish);
}
ip_rcv_finish
---ip_rcv_finish_core
------ip_route_input_noref
----------ip_route_input_rcu
-------------ip_route_input_slow  主要完成路由查找,针对vxlan报文目的IP地址为本机IP地址
----------------rt_dst_alloc   rt_dst_alloc  函数非常重要,主要指定RTCF_LOCAL类型的报文接下里的处理函数ip_local_deliverstruct rtable *rt_dst_alloc(struct net_device *dev,unsigned int flags, u16 type,bool nopolicy, bool noxfrm, bool will_cache)
{struct rtable *rt;rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK,(will_cache ? 0 : DST_HOST) |(nopolicy ? DST_NOPOLICY : 0) |(noxfrm ? DST_NOXFRM : 0));if (rt) {rt->rt_genid = rt_genid_ipv4(dev_net(dev));rt->rt_flags = flags;rt->rt_type = type;rt->rt_is_input = 0;rt->rt_iif = 0;rt->rt_pmtu = 0;rt->rt_mtu_locked = 0;rt->rt_gateway = 0;rt->rt_uses_gateway = 0;INIT_LIST_HEAD(&rt->rt_uncached);rt->dst.output = ip_output;if (flags & RTCF_LOCAL)rt->dst.input = ip_local_deliver;}return rt;
}
static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{struct net_device *dev = skb->dev;int ret;/* if ingress device is enslaved to an L3 master device pass the* skb to its handler for processing*/skb = l3mdev_ip_rcv(skb);if (!skb)return NET_RX_SUCCESS;ret = ip_rcv_finish_core(net, sk, skb, dev);if (ret != NET_RX_DROP) //查询到路由信息返回OK,所以会调用下面的dst_inputret = dst_input(skb);  return ret;
}dst_input函数即为前面注册的ip_local_deliver
static inline int dst_input(struct sk_buff *skb)
{return skb_dst(skb)->input(skb);
}
ip_local_deliver
---ip_local_deliver_finishstatic int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{__skb_pull(skb, skb_network_header_len(skb));rcu_read_lock();{int protocol = ip_hdr(skb)->protocol;const struct net_protocol *ipprot;int raw;resubmit:raw = raw_local_deliver(skb, protocol);ipprot = rcu_dereference(inet_protos[protocol]); //根据报文协议类型进行处理,调用各自的回调if (ipprot) {int ret;if (!ipprot->no_policy) {if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {kfree_skb(skb);goto out;}nf_reset(skb);}ret = ipprot->handler(skb);if (ret < 0) {protocol = -ret;goto resubmit;}__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);} else {if (!raw) {if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {__IP_INC_STATS(net, IPSTATS_MIB_INUNKNOWNPROTOS);icmp_send(skb, ICMP_DEST_UNREACH,ICMP_PROT_UNREACH, 0);}kfree_skb(skb);} else {__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);consume_skb(skb);}}}out:rcu_read_unlock();return 0;
}inet_protos[protocol]数组中记录UDP/TCP/ICMP等各种协议类型对应的处理函数(handler)if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)pr_crit("%s: Cannot add ICMP protocol\n", __func__);if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)pr_crit("%s: Cannot add UDP protocol\n", __func__);if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)pr_crit("%s: Cannot add TCP protocol\n", __func__);static struct net_protocol udp_protocol = {.early_demux =  udp_v4_early_demux,.early_demux_handler =  udp_v4_early_demux,.handler =  udp_rcv,.err_handler = udp_err,.no_policy =   1,.netns_ok =  1,
};udp类型的报文对应的handler 为udp_rcv,所有的udp报文均由udp_rcv处理
udp_rcv
---__udp4_lib_rcv
------__udp4_lib_lookup_skb //根据端口号获取sk
---------udp_unicast_rcv_skb
------------udp_queue_rcv_skbstatic int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{struct udp_sock *up = udp_sk(sk);int is_udplite = IS_UDPLITE(sk);/** Charge it to the socket, dropping if the queue is full.*/if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))goto drop;nf_reset(skb);if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) { //tunnel报文处理int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);/** This is an encapsulation socket so pass the skb to* the socket's udp_encap_rcv() hook. Otherwise, just* fall through and pass this up the UDP socket.* up->encap_rcv() returns the following value:* =0 if skb was successfully passed to the encap*    handler or was discarded by it.* >0 if skb should be passed on to UDP.* <0 if skb should be resubmitted as proto -N*//* if we're overly short, let UDP handle it */encap_rcv = READ_ONCE(up->encap_rcv);if (encap_rcv) {int ret;/* Verify checksum before giving to encap */if (udp_lib_checksum_complete(skb))goto csum_error;ret = encap_rcv(sk, skb);if (ret <= 0) {__UDP_INC_STATS(sock_net(sk),UDP_MIB_INDATAGRAMS,is_udplite);return -ret;}}/* FALLTHROUGH -- it's a UDP Packet */}/**   UDP-Lite specific tests, ignored on UDP sockets*/if ((up->pcflag & UDPLITE_RECV_CC)  &&  UDP_SKB_CB(skb)->partial_cov) {/** MIB statistics other than incrementing the error count are* disabled for the following two types of errors: these depend* on the application settings, not on the functioning of the* protocol stack as such.** RFC 3828 here recommends (sec 3.3): "There should also be a* way ... to ... at least let the receiving application block* delivery of packets with coverage values less than a value* provided by the application."*/if (up->pcrlen == 0) {          /* full coverage was set  */net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n",UDP_SKB_CB(skb)->cscov, skb->len);goto drop;}/* The next case involves violating the min. coverage requested* by the receiver. This is subtle: if receiver wants x and x is* greater than the buffersize/MTU then receiver will complain* that it wants x while sender emits packets of smaller size y.* Therefore the above ...()->partial_cov statement is essential.*/if (UDP_SKB_CB(skb)->cscov  <  up->pcrlen) {net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n",UDP_SKB_CB(skb)->cscov, up->pcrlen);goto drop;}}prefetch(&sk->sk_rmem_alloc);if (rcu_access_pointer(sk->sk_filter) &&udp_lib_checksum_complete(skb))goto csum_error;if (sk_filter_trim_cap(sk, skb, sizeof(struct udphdr)))goto drop;udp_csum_pull_header(skb);ipv4_pktinfo_prepare(sk, skb);return __udp_queue_rcv_skb(sk, skb);csum_error:__UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
drop:__UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);atomic_inc(&sk->sk_drops);kfree_skb(skb);return -1;
}在创建vxlan接口时会注册encap_rcv 回调函数,并且设置encap_type 为1static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,__be16 port, u32 flags)
{struct vxlan_net *vn = net_generic(net, vxlan_net_id);struct vxlan_sock *vs;struct socket *sock;unsigned int h;struct udp_tunnel_sock_cfg tunnel_cfg;vs = kzalloc(sizeof(*vs), GFP_KERNEL);if (!vs)return ERR_PTR(-ENOMEM);for (h = 0; h < VNI_HASH_SIZE; ++h)INIT_HLIST_HEAD(&vs->vni_list[h]);sock = vxlan_create_sock(net, ipv6, port, flags);if (IS_ERR(sock)) {kfree(vs);return ERR_CAST(sock);}vs->sock = sock;refcount_set(&vs->refcnt, 1);vs->flags = (flags & VXLAN_F_RCV_FLAGS);spin_lock(&vn->sock_lock);hlist_add_head_rcu(&vs->hlist, vs_head(net, port));udp_tunnel_notify_add_rx_port(sock,(vs->flags & VXLAN_F_GPE) ?UDP_TUNNEL_TYPE_VXLAN_GPE :UDP_TUNNEL_TYPE_VXLAN);spin_unlock(&vn->sock_lock);/* Mark socket as an encapsulation socket. */memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));tunnel_cfg.sk_user_data = vs;tunnel_cfg.encap_type = 1;tunnel_cfg.encap_rcv = vxlan_rcv;tunnel_cfg.encap_destroy = NULL;tunnel_cfg.gro_receive = vxlan_gro_receive;tunnel_cfg.gro_complete = vxlan_gro_complete;setup_udp_tunnel_sock(net, sock, &tunnel_cfg);return vs;
}void setup_udp_tunnel_sock(struct net *net, struct socket *sock,struct udp_tunnel_sock_cfg *cfg)
{struct sock *sk = sock->sk;/* Disable multicast loopback */inet_sk(sk)->mc_loop = 0;/* Enable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */inet_inc_convert_csum(sk);rcu_assign_sk_user_data(sk, cfg->sk_user_data);udp_sk(sk)->encap_type = cfg->encap_type;udp_sk(sk)->encap_rcv = cfg->encap_rcv;udp_sk(sk)->encap_destroy = cfg->encap_destroy;udp_sk(sk)->gro_receive = cfg->gro_receive;udp_sk(sk)->gro_complete = cfg->gro_complete;udp_tunnel_encap_enable(sock);
}

/* Callback from net/ipv4/udp.c to receive packets */
static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
{struct pcpu_sw_netstats *stats;struct vxlan_dev *vxlan;struct vxlan_sock *vs;struct vxlanhdr unparsed;struct vxlan_metadata _md;struct vxlan_metadata *md = &_md;__be16 protocol = htons(ETH_P_TEB);bool raw_proto = false;void *oiph;__be32 vni = 0;/* Need UDP and VXLAN header to be present */if (!pskb_may_pull(skb, VXLAN_HLEN))goto drop;unparsed = *vxlan_hdr(skb);/* VNI flag always required to be set */if (!(unparsed.vx_flags & VXLAN_HF_VNI)) {netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n",ntohl(vxlan_hdr(skb)->vx_flags),ntohl(vxlan_hdr(skb)->vx_vni));/* Return non vxlan pkt */goto drop;}unparsed.vx_flags &= ~VXLAN_HF_VNI;unparsed.vx_vni &= ~VXLAN_VNI_MASK;vs = rcu_dereference_sk_user_data(sk);if (!vs)goto drop;vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni); //查找vxlan设备if (!vxlan)goto drop;/* For backwards compatibility, only allow reserved fields to be* used by VXLAN extensions if explicitly requested.*/if (vs->flags & VXLAN_F_GPE) {if (!vxlan_parse_gpe_hdr(&unparsed, &protocol, skb, vs->flags))goto drop;raw_proto = true;}if (__iptunnel_pull_header(skb, VXLAN_HLEN, protocol, raw_proto,!net_eq(vxlan->net, dev_net(vxlan->dev))))goto drop;if (vxlan_collect_metadata(vs)) {struct metadata_dst *tun_dst;tun_dst = udp_tun_rx_dst(skb, vxlan_get_sk_family(vs), TUNNEL_KEY,key32_to_tunnel_id(vni), sizeof(*md));  //收集tunnel信息,其中有外层的源目IP地址和源目端口号if (!tun_dst)goto drop;md = ip_tunnel_info_opts(&tun_dst->u.tun_info);skb_dst_set(skb, (struct dst_entry *)tun_dst);} else {memset(md, 0, sizeof(*md));}if (vs->flags & VXLAN_F_REMCSUM_RX)if (!vxlan_remcsum(&unparsed, skb, vs->flags))goto drop;if (vs->flags & VXLAN_F_GBP)vxlan_parse_gbp_hdr(&unparsed, skb, vs->flags, md);/* Note that GBP and GPE can never be active together. This is* ensured in vxlan_dev_configure.*/if (unparsed.vx_flags || unparsed.vx_vni) {/* If there are any unprocessed flags remaining treat* this as a malformed packet. This behavior diverges from* VXLAN RFC (RFC7348) which stipulates that bits in reserved* in reserved fields are to be ignored. The approach here* maintains compatibility with previous stack code, and also* is more robust and provides a little more security in* adding extensions to VXLAN.*/goto drop;}if (!raw_proto) {if (!vxlan_set_mac(vxlan, vs, skb, vni))goto drop;} else {skb_reset_mac_header(skb);skb->dev = vxlan->dev;  //更改dev为vxlan->devskb->pkt_type = PACKET_HOST;}oiph = skb_network_header(skb);skb_reset_network_header(skb);if (!vxlan_ecn_decapsulate(vs, oiph, skb)) {++vxlan->dev->stats.rx_frame_errors;++vxlan->dev->stats.rx_errors;goto drop;}rcu_read_lock();if (unlikely(!(vxlan->dev->flags & IFF_UP))) {rcu_read_unlock();atomic_long_inc(&vxlan->dev->rx_dropped);goto drop;}stats = this_cpu_ptr(vxlan->dev->tstats);u64_stats_update_begin(&stats->syncp);stats->rx_packets++;stats->rx_bytes += skb->len;u64_stats_update_end(&stats->syncp);gro_cells_receive(&vxlan->gro_cells, skb);rcu_read_unlock();return 0;drop:/* Consume bad packet */kfree_skb(skb);return 0;
}

总结:vxlan收包流量涉及的函数

#rx softirq处理,一起捅到udp层,把skb上的dev切换成vxlan device,再入backlog,触发softirq
process_backlog└─__netif_receive_skb└─__netif_receive_skb_core└─deliver_skb└─(pt_prev->func)ip_rcv└─ip_rcv_finish└─ip_route_input_noref└─ip_route_input_slow└─ip_local_deliver└─ip_local_deliver_finish└─(ipprot->handler)udp_rcv└─__udp4_lib_rcv└─udp_queue_rcv_skb└─vxlan_rcv├─skb->dev = vxlan->dev;//这儿skb的切换到vxlan device└─gro_cells_receive└─netif_rx└─netif_rx_internal└─enqueue_to_backlog└─____napi_schedule报文解封vxlan后重新进入协议栈处理
process_backlog└─__netif_receive_skb└─__netif_receive_skb_core└─(rx_handler)netdev_frame_hook //ovs创建的vxlan口会有一个回调(报文通过回调送入ovs处理),普通内核创建的vxlan口没有注册回调,会根据默认的协议栈流程进行处理└─netdev_port_receive└─ovs_vport_receive├─ovs_flow_key_extract└─ovs_dp_process_packet

2.vxlan发包处理流程

由vxlan注册函数可知,其发送接口为vxlan_xmit
static const struct net_device_ops vxlan_netdev_ops = {.ndo_init       = vxlan_init,.ndo_open     = vxlan_open,.ndo_stop     = vxlan_stop,.ndo_start_xmit       = vxlan_xmit,.ndo_get_stats64  = ip_tunnel_get_stats64,.ndo_set_rx_mode   = vxlan_set_multicast_list,.ndo_change_mtu     = eth_change_mtu,.ndo_validate_addr    = eth_validate_addr,.ndo_set_mac_address   = eth_mac_addr,.ndo_fdb_add        = vxlan_fdb_add,.ndo_fdb_del       = vxlan_fdb_delete,.ndo_fdb_dump       = vxlan_fdb_dump,
};
vxlan_xmit└─vxlan_xmit_one├─vxlan_build_skb└─udp_tunnel_xmit_skb└─iptunnel_xmit└─ip_local_out_sk

补充说明下ovs上的vxlan发包流程

datapath执行output

do_execute_actions└─do_output└─ovs_vport_send├─skb->dev = vport->dev;//把dev替换成内核net列表里的dev └─dev_queue_xmit└─__dev_queue_xmit├─validate_xmit_skb├─dev_hard_start_xmit|   └─xmit_one|       └─netdev_start_xmit|           └─ndo_start_xmit就是vxlan_xmit└─dev_xmit_complete

后面的流程与上面内核中的vxlan接口发包流程一致

vxlan_xmit└─vxlan_xmit_one├─vxlan_build_skb└─udp_tunnel_xmit_skb└─iptunnel_xmit└─ip_local_out_sk

由此可见ovs的vxlan处理也使用了内核的vxlan处理流程

3 vxlan接口创建

内核方式创建vxlan接口

ip link add vxlan1 type vxlan id 1 dstport 4789 remote 10.10.10.9 local 10.10.10.11 dev eth0 1> vxlan1 即为创建的接口名称 ,type 为 vxlan 类型。
2> id 即为 VNI。
3> dstport 指定 UDP的目的端口,IANA 为vxlan分配的目的端口是 4789。
4> remote 和 local ,即远端和本地的IP地址,因为vxlan是MAC-IN-UDP,需要指定外层IP,此处即指。
dev 本地流量接口。用于 vtep 通信的网卡设备,用来读取 IP 地址。注意这个参数和 local参数含义是相同的,在这里写出来是为了告诉大家有两个参数存在。

ovs下创建vxlan接口

ovs-vsctl add-port br1 vx1 -- set interface vx1 type=vxlan options:remote_ip=172.16.0.39 options:key=100创建完成后发现内核态也会生成一个vxlan接口,其vni值为0ip -d link show vxlan_sys_4789
8: vxlan_sys_4789: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65535 qdisc noqueue master ovs-system state UNKNOWN mode DEFAULT qlen 1000link/ether 0e:49:c4:05:a6:87 brd ff:ff:ff:ff:ff:ff promiscuity 1 vxlan id 0 srcport 0 0 dstport 4789 nolearning ageing 300 udpcsum udp6zerocsumrx openvswitch_slave addrgenmode eui64
[root@p57093v weiyanhua]# 

上面的例子中通过使用ovs-vsctl命令创建了一个vxlan,那ovs-vsctl添加一个vxlan port,ovs-vswitchd怎么知道的,答案就是ovs-vswitch和ovsdb-server之间也有连接,ovs-vswitch通过IDL感知ovsdb-server的变化。

ovs-vswitchd main线程有个while(1)处理ovsdb-server变化和ovs-appctl命令,ovs-vsctl创建一个ovs vxlan port,最终调用到了dpif_netlink_port_add。

main├─unixctl_server_create├─bridge_init└─while//特别注意这个while循环├─bridge_run|  ├─ovsdb_idl_run|  └─bridge_reconfigure|     └─bridge_add_ports|        └─iface_create|           └─ofproto_port_add|              └─(ofproto_class->port_add)port_add|                 └─dpif_port_add|                     └─(dpif_class->port_add)dpif_netlink_port_add├─unixctl_server_run|   └─run_connection|      └─process_command|         └─(command->cb)├─netdev_run├─memory_wait├─bridge_wait├─unixctl_server_wait├─netdev_wait└─pool_block

dpif_netlink_port_add用netlink发送给内核

dpif_netlink_port_add└─dpif_netlink_rtnl_port_create_and_add└─dpif_netlink_rtnl_port_create├─dpif_netlink_rtnl_create|   └─dpif_netlink_rtnl_create//注意这个RTM_NEWLINK和IFF_UP└─dpif_netlink_port_add__//通知内核ovs添加vxlan_vport

内核ovs模块初始化,内核处理ovs route netlink发送来的添加vxlan口的消息,创建一个vxlan device还要创建一个vport device,vport device里嵌套 vxlan device,内核openvswitch模块处理添加vport

ovs_vport_cmd_new└─new_vport└─ovs_vport_add└─vxlan_create├─vxlan_tnl_create|   ├─vxlan_dev_create|   └─dev_change_flags|       └─__dev_change_flags//UP这个口|           └─__dev_open|               ├─(ops->ndo_open)也就是vxlan_open|               └─netpoll_rx_enable└─ovs_netdev_link├─vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);├─netdev_rx_handler_register(vport->dev, netdev_frame_hook,vport);├─dev_disable_lro└─dev_set_promiscuity

上面分析了ovs创建vxlan接口的主要处理流量,接下来简单看下内核创建vxlan接口的流程

vxlan_init_module└─rtnl_link_register(&vxlan_link_ops)
rtnl_newlink├─rtnl_create_link|   ├─dev=kzalloc()|   └─alloc_netdev_mqs|       └─vxlan_setup├─(ops->newlink)vxlan_newlink└─rtnl_configure_link└─__dev_change_flags└─__dev_open└─(ops->ndo_open)vxlan_open

linux 内核vxlan收发包流程相关推荐

  1. Linux 网络层收发包流程及 Netfilter 框架浅析

    本文作者:sivenzhang,腾讯 IEG 测试开发工程师 1. 前言 本文主要对 Linux 系统内核协议栈中网络层接收,发送以及转发数据包的流程进行简要介绍,同时对 Netfilter 数据包过 ...

  2. Linux网络层收发包流程及Netfilter框架浅析

    1. 前言 本文主要对 Linux 系统内核协议栈中网络层接收,发送以及转发数据包的流程进行简要介绍,同时对 Netfilter 数据包过滤框架的基本原理以及使用方式进行简单阐述. 内容如有理解错误而 ...

  3. 如何快速优化 Linux 内核 UDP 收包效率? | CSDN 博文精选

    作者 | dog250 责编 | 郭芮 出品 | CSDN 博客 现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈 ...

  4. Linux内核UDP收包为什么效率低?能做什么优化?

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK. 但是,即便Linux内核协议栈收包 ...

  5. Linux网络开始收发包之前需要做的事情——创建ksoftirqd内核进程

    Linux网络开始收发包之前需要做的事情 1.创建ksoftirqd内核进程 2.网络子系统初始化 3.内核网络协议栈注册 4.网卡驱动初始化 5.启动网卡 以上5步就是我们在收发包之前内核要做的事情 ...

  6. ixgbe网卡驱动 Ⅳ----收发包流程详解

    目录 1 网卡队列收包流程概述 2 ixgbe_ring 结构 3 ixgbe 驱动收包流程 3.1 硬件中断入口 ixgbe_msix_clean_rings/ixgbe_intr 3.2 软中断入 ...

  7. linux内核协议栈接收数据流程(一)

    linux网络接收数据第一站--网卡驱动 linux网络接收数据流程的第一站为网卡驱动,网卡接收包流程大致为: 网卡硬件接收到包,会将数据包通过DMA映射到预先分配好的ringbuffer内存环形缓存 ...

  8. linux内核网络协议栈--发送流程及函数(十)

    本章会一步一步的分析,在linux内核中,数据是如何从网络中接收并最后到达应用程序的. 用户数据的发送流程如下图所示,不管是tfp,telnet,http都是类似的.当然我们在使用应用的时候,根本不会 ...

  9. linux内核下网络驱动流程,基于Linux内核驱动的网络带宽测速方法与流程

    本发明涉及一种测速方法,尤其是一种网络带宽测速方法. 背景技术: :电信运营商为客户提供一定带宽的Internet接入:为了检验带宽是否达标,一般均由客户使用个人电脑在网页上直接测速.但是随着智能网关 ...

最新文章

  1. C++STL常用遍历算法
  2. python编程内置函数使用方法_python编程(4)--内置函数
  3. try except python3.7_python3.X中try/except
  4. Extjs4 MVC 添加view层
  5. 讲100个科学道理,不如做这些有趣的理科实验!
  6. 新Rider预览版发布,对F#的支持是亮点
  7. 海上瓶子下有东西吗_《海上钢琴师》:宁愿一生孤独,不愿随波逐流。
  8. ssh自动登陆和scp自动拷贝文件
  9. Zerotier Moon服务器配置
  10. 对存在过期 binlog 的 MySQL5.7 添加从服务器
  11. C语言的32个关键字和9种控制语句
  12. ic卡识别程序c语言,如何读写接触式IC卡
  13. qt中drawline函数的参数_c++ - Qt-drawPolyline和drawLine之间的区别 - 堆栈内存溢出
  14. 安装SQL Server 2016及一些常用操作
  15. 快速打开ofd文件发票转换pdf格式发票方法
  16. 【论文学习】干旱指标相关
  17. 关于eclipse无法连接手机调试问题
  18. linux内核如何读写ddr,linux内核解压详解.doc
  19. 多维数组java_Java多维数组
  20. ThinkPHP 入门

热门文章

  1. html5canvas绘制时钟,JavaScript html5 canvas绘制时钟效果
  2. 如果有蛇吞象...TOPKU对这次联想收购IBM的看法
  3. Codeforces Round #827 (Div. 4)
  4. 自动排产软件中的电子看板
  5. 2018.8.21 2018暑假集训之滑雪
  6. 数据库语句常见方法:(随机生成Id,结合CSAE...WHEN的使用,分组拼接字段数据
  7. 用友U8老版本升级 单据表头栏目显示名称空白,例如:发货单的表头客户名称.这几个如果是必须项目就是空白.
  8. PouchContainer GA 版本发布,邀您参与上海 Meetup 共话容器未来
  9. 【Linux】安装 Docker Registry
  10. 冲击波病毒内幕点滴(4) (转)