日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从Openvswitch代码看网络包的旅程

發布時間:2025/7/14 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从Openvswitch代码看网络包的旅程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們知道,Openvwitch可以創建虛擬交換機,而網絡包可以通過虛擬交換機進行轉發,并通過流表進行處理,具體的過程如何呢?

?

一、內核模塊Openvswitch.ko的加載

?

OVS是內核態和用戶態配合工作的,所以首先要加載內核態模塊Openvswitch.ko。

?

在datapath/datapath.c中會調用module_init(dp_init);來初始化內核模塊。

?

其中比較重要的是調用了dp_register_genl(),這個就是注冊netlink函數,從而用戶態進程ovs-vswitchd可以通過netlink調用內核。

?

這里dp_genl_families由四個netlink的family組成

?

  • static?struct?genl_family *dp_genl_families[] = {

  • ???&dp_datapath_genl_family,

  • ???&dp_vport_genl_family,

  • ???&dp_flow_genl_family,

  • ???&dp_packet_genl_family,

  • };

  • 可以看出,在內核中,包含對datapath的操作,例如OVS_DP_CMD_NEW,對虛擬端口vport的操作,例如OVS_VPORT_CMD_NEW,對flow流表的操作,例如OVS_FLOW_CMD_NEW,對packet包的操作,例如OVS_PACKET_CMD_EXECUTE。

    ?

    二、用戶態進程ovs-vswitchd的啟動

    ?

    ovs-vswitchd.c的main函數最終會進入一個while循環,在這個無限循環中,里面最重要的兩個函數是bridge_run()和netdev_run()。

    ?

    Openvswitch主要管理兩種類型的設備,一個是創建的虛擬網橋,一個是連接到虛擬網橋上的設備。

    ?

    其中bridge_run就是初始化數據庫中已經創建的虛擬網橋。

    ?

    虛擬網卡的初始化則靠netdev_run()。

    ?

    bridge_run會調用static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg),其中ovs_cfg是從ovsdb-server里面讀取出來的配置。

    ?

    在這個函數里面,對于每一個網橋,將網卡添加進去。

    ?

  • HMAP_FOR_EACH (br, node, &all_bridges) {

  • ????bridge_add_ports(br, &br->wanted_ports);

  • ????shash_destroy(&br->wanted_ports);

  • }

  • ?

    最終會調用dpif_netlink_port_add__在這個函數里面,會調用netlink的API,命令為OVS_VPORT_CMD_NEW。

    ?

    三、內核模塊監聽網卡

    ?

    ovs-vswitchd啟動的時候,將虛擬網卡添加到虛擬交換機上的時候,會調用netlink的OVS_VPORT_CMD_NEW命令,因而會調用函數ovs_vport_cmd_new。

    ?

    它最終會調用ovs_netdev_link,其中有下面的代碼:

    ?

  • err = netdev_rx_handler_register(vport->dev, netdev_frame_hook,

  • ????????????????vport);

  • ?

    注冊一個方法叫做netdev_frame_hook,每當網卡收到包的時候,就調用這個方法。

    ?

    四、內核態網絡包處理

    ?

    Openvswitch的內核模塊openvswitch.ko會在網卡上注冊一個函數netdev_frame_hook,每當有網絡包到達網卡的時候,這個函數就會被調用。

    ?

  • static?struct?sk_buff *netdev_frame_hook(struct?sk_buff *skb)

  • {

  • ???if?(unlikely(skb->pkt_type == PACKET_LOOPBACK))

  • ??????return?skb;

  • ?

  • ???port_receive(skb);

  • ???return?NULL;

  • }

  • ?

    調用port_receive即是調用netdev_port_receive

    ?

    在這個函數里面,首先聲明了變量struct sw_flow_key key;

    如果我們看這個key的定義,可見這個key里面是一個大雜燴,數據包里面的幾乎任何部分都可以作為key來查找flow表

    • tunnel可以作為key

    • 在物理層,in_port即包進入的網口的ID

    • 在MAC層,源和目的MAC地址

    • 在IP層,源和目的IP地址

    • 在傳輸層,源和目的端口號

    • IPV6

    所以,要在內核態匹配流表,首先需要調用ovs_flow_key_extract,從包的正文中提取key的值。

    ?

    接下來就是要調用ovs_dp_process_packet了。

    ?

    這個函數首先在內核里面的流表中查找符合key的flow,也即ovs_flow_tbl_lookup_stats,如果找到了,很好說明用戶態的流表已經放入內核,則走fast path就可了。于是直接調用ovs_execute_actions,執行這個key對應的action。

    ?

    如果不能找到,則只好調用ovs_dp_upcall,讓用戶態去查找流表。會調用static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info)

    ?

    它會調用err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);通過netlink將消息發送給用戶態。在用戶態,有線程監聽消息,一旦有消息,則觸發udpif_upcall_handler。

    ?

    Slow Path & Fast Path

    ?

    ?

    ?

    Slow Path:

    當Datapath找不到flow rule對packet進行處理時

    Vswitchd使用flow rule對packet進行處理。

    ?

    Fast Path:

    將slow path的flow rule放在內核態,對packet進行處理

    ?

    Unknown Packet Processing

    Datapath使用flow rule對packet進行處理,如果沒有,則有vswitchd使用flow rule進行處理

    ?

    ?

  • 從Device接收Packet交給事先注冊的event handler進行處理

  • 接收Packet后識別是否是unknown packet,是則交由upcall處理

  • vswitchd對unknown packet找到flow rule進行處理

  • 將Flow rule發送給datapath

  • ?

    五、用戶態處理包

    ?

    當內核無法查找到流表項的時候,則會通過upcall來調用用戶態ovs-vswtichd中的flow table。

    ?

    會調用ofproto-dpif-upcall.c中的udpif_upcall_handler函數。

    ?

    (1) 首先讀取upcall調用static int upcall_receive(struct upcall *upcall, const struct dpif_backer *backer, const struct dp_packet *packet, enum dpif_upcall_type type, const struct nlattr *userdata, const struct flow *flow, const unsigned int mru, const ovs_u128 *ufid, const unsigned pmd_id)

    ?

    (2) 其次提取包頭調用void flow_extract(struct dp_packet *packet, struct flow *flow),提取出的flow如下:

    ?

  • ? ??/* L2, Order the same as in the Ethernet header! (64-bit aligned) */

  • ????struct?eth_addr dl_dst;?/* Ethernet destination address. */

  • ????struct?eth_addr dl_src;?/* Ethernet source address. */

  • ????ovs_be16 dl_type;?/* Ethernet frame type. */

  • ????ovs_be16 vlan_tci;?/* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */

  • ????ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)];?/* MPLS label stack

  • ?????????????????????????????????????????????????????????????(with padding). */

  • ????/* L3 (64-bit aligned) */

  • ????ovs_be32 nw_src;?/* IPv4 source address. */

  • ????ovs_be32 nw_dst;?/* IPv4 destination address. */

  • ????struct?in6_addr ipv6_src;?/* IPv6 source address. */

  • ????struct?in6_addr ipv6_dst;?/* IPv6 destination address. */

  • ????ovs_be32 ipv6_label;?/* IPv6 flow label. */

  • ????uint8_t nw_frag;?/* FLOW_FRAG_* flags. */

  • ????uint8_t nw_tos;?/* IP ToS (including DSCP and ECN). */

  • ????uint8_t nw_ttl;?/* IP TTL/Hop Limit. */

  • ????uint8_t nw_proto;?/* IP protocol or low 8 bits of ARP opcode. */

  • ????struct?in6_addr nd_target;?/* IPv6 neighbor discovery (ND) target. */

  • ????struct?eth_addr arp_sha;?/* ARP/ND source hardware address. */

  • ????struct?eth_addr arp_tha;?/* ARP/ND target hardware address. */

  • ????ovs_be16 tcp_flags;?/* TCP flags. With L3 to avoid matching L4. */

  • ????ovs_be16 pad3;?/* Pad to 64 bits. */

  • ?

  • ????/* L4 (64-bit aligned) */

  • ????ovs_be16 tp_src;?/* TCP/UDP/SCTP source port/ICMP type. */

  • ????ovs_be16 tp_dst;?/* TCP/UDP/SCTP destination port/ICMP code. */

  • ????ovs_be32 igmp_group_ip4;?/* IGMP group IPv4 address.

  • ?????????????????????????????????* Keep last for BUILD_ASSERT_DECL below. */

    ?

  • ?

    (3) 然后調用static int process_upcall(struct udpif *udpif, struct upcall *upcall, struct ofpbuf *odp_actions, struct flow_wildcards *wc)來處理upcall。

    ?

    對于MISS_UPCALL,調用static void upcall_xlate(struct udpif *udpif, struct upcall *upcall, struct ofpbuf *odp_actions, struct flow_wildcards *wc)

    ?

    會調用enum xlate_error xlate_actions(struct xlate_in *xin, struct xlate_out *xout)

    在這個函數里面,會在flow table里面查找rule

    ctx.rule = rule_dpif_lookup_from_table( ctx.xbridge->ofproto, ctx.tables_version, flow, xin->wc, ctx.xin->resubmit_stats, &ctx.table_id, flow->in_port.ofp_port, true, true);

    找到rule之后,調用static void do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, struct xlate_ctx *ctx)在這個函數里面,根據action的不同,修改flow的內容。

    ?

    (4) 最后調用static void handle_upcalls(struct udpif *udpif, struct upcall *upcalls, size_t n_upcalls)將flow rule添加到內核中的datapath

    他會調用void dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops),他會調用dpif->dpif_class->operate(dpif, ops, chunk);

    ?

    會調用dpif_netlink_operate()

    ?

    會調用netlink修改內核中datapath的規則。

    ?

  • case?DPIF_OP_FLOW_PUT:

  • ????put = &op->u.flow_put;

  • ????dpif_netlink_init_flow_put(dpif, put, &flow);

  • ????if?(put->stats) {

  • ????????flow.nlmsg_flags |= NLM_F_ECHO;

  • ????????aux->txn.reply = &aux->reply;

  • ????}

  • ????dpif_netlink_flow_to_ofpbuf(&flow, &aux->request);

  • ????break;

  • ?

    歡迎關注個人公眾號

    ?

    ?

    轉載于:https://www.cnblogs.com/popsuper1982/p/8948016.html

    總結

    以上是生活随笔為你收集整理的从Openvswitch代码看网络包的旅程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。