Preface
這邊要探討的是當網卡收到封包後,在 KERNEL 中由下往上的流程
netdev_frame_hook
1 | static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb) |
- net_device收到封包後,變變呼叫這個fucntion call來處理。
- 先判斷是不是LOOPBACK的,是的話就不需要處理了。
- 透過 ovs_netdev_get_vport取得該dev對應的vport
- 呼叫 netdev_port_receive來處理這個封包
netdev_port_receive
1 | static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) |
- skb_warn_if_lro判斷其LRO的設定有沒有問題
- skb_push調整skb中的data指標
- ovs_skb_postpush_rcsum 處理ip checksum。
- 呼叫 ovs_vport_receive繼續處理
ovs_vport_receive
1 | void ovs_vport_receive(struct vport *vport, struct sk_buff *skb, |
- struct pcpu_tstats 紀錄當前cpu對於封包的一些計數 (rx,tx)
- 由 this_cpu_ptr取得這個vport的(tx,rx) counter.
- Update counters (packets,bytes)
- 透過 ovs_dp_process_received_packet繼續處理
ovs_dp_process_received_packet
- 這個function的目的就是
- 由skb內取出封包各欄位的資訊(L2,L3,L4)
- 去查詢該 datapath的flow table中否有符合的flow entry
- 有找到,就執行對應的action.
- 沒有找到,就執行 ovs_dp_upcall送到 user space
- 由skb內取出封包各欄位的資訊(L2,L3,L4)
1 | void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) |
- 先由 vport 取得 對應的 datapath
- 由 this_cpu_ptr取得這個datapath的(tx,rx) counter.
- 透過 ovs_flow_extract 把 skb, vport 的資訊都寫入 sw_flow_key key之中
- 呼叫 ovs_flow_lookup 去查詢這個packet在table之中有沒有match的flow entry.
- 如果沒有找到,那就透過 upcall的方式,把這個flow_miss告訴 ovs-vswitchd去處理
- 如果有找到,先透過 ovs_flow_used更新該flow的一些資訊(usedtime,packet,byte,tcp_flag),接者在呼叫 ovs_execute_actions 執行這個flow 對應的actions
ovs_flow_extract
1 | int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) |
- OVS_CB是一個marco,會把skbuff中的cb區域拿來使用,並且轉型成 ovs_skb_cb
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb) - 如果該packet有使用 tunnel_key的話,就把該 tun_key給複製到 key。
- 把收到封包的port number也記錄到key裡面( ingress port)
- 使用 skb_reset_mac_header 得到 mac header (放在 skb->mac_header)
1 | /* Link layer. We are guaranteed to have at least the 14 byte Ethernet |
- 取得 ethernet header,並且把 sourcee/destinaion mac address給複製到key。
- 透過 __skb_pull,把 skb-data給往下指 ETH_ALEN*2
- 檢查有沒有用 vlan tag,有的話就把tci給抓近來
- 使用 parse_ethertype 得到該封包的 ethernet type
- 使用 skb_reset_network_header 得到 network header (放在 skb->network_header)
- 透過 __skb_push 把skb中的data指標往上移(這樣可以取回mac header的一些資訊),供未來使用
- 後面就是針對 (IP,IPV6,ARP)等在做更細部的資料取得
1 | struct sw_flow *ovs_flow_lookup(struct flow_table *tbl, |
- 從 datapath的flow table中先取得所有的 mask_list
- 使用 ovs_masked_flow_lookup去搜尋進來的封包是否有match
- 最後回傳 flow.
1 | static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table, |
- OVS 2.0後增加對megaflow的支持,所以在kernel端也可以支援wildcard的flow matching.
- sw_flow_key實際上就是個wildcard,每個進來的封包都先跟wildcard做 ovs_flow_key_mask,然後在用mask後的結果去table中尋找有沒有可以match的
- 使用mask過後的結果來做hash,並且透過 find_bucket找到那個hash值所在的bucket
- 針對那個bucket中所有的flow去做比對,如果 mask相同且 flow_cmpmasked結果為真,就代表match.
- key的start & end 還不是很明瞭其目的以及用途,待釐清
1 | void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src, |
- 把 src用 mask去處理,結果放到 dst上
- 這邊可以看到做mask的方法就是不停地用 &來取結果而已。
Found
尋找到flow後
- 更新該flow的一些統計資訊 ( ovs_flow_used)
- 執行該flow entry上的actions (ovs_execute_actions)
ovs_flow_used
1 | void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) |
- 如果該封包滿足(IP/IPV6,TCP)且TCP有額外的flag的話,就更新其TCP_FLAGS
- 更新該flow的相關資訊
- used(上次使用時間),單位是 jiffies
- counter.
ovs_execute_actions
1 | /* Execute a list of actions against 'skb'. */ |
- 先從flow_sf_acts中取出對應的actions(sw_flow_actions)
- 這邊會限制執行 do_execute_actions的次數,設計理念尚未明瞭。
- 呼叫 do_execute_actions來做後續的處理
do_execute_actions
1 | static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, |
- flow的action都是透過nlattr來儲存,這是netlink相關的資料結構,因為 user space也會透過netlink的方式要求kernel直接處理封包,所以action都用 nlattr來處理
- 因為可以 output到多個port去,每次都會需要複製 skb,所以這邊使用 prev_port來處理只有一次 output的情況(不用複製)
Not found
如果沒有找到該flow,kernel就會透過netlink的方式,把這個封包送到 ovs-vswitched去處理。
1 | struct dp_upcall_info upcall; |
1 | struct dp_upcall_info { |
- 每個 dp_upcall_info都是透過 netlink的方式把資料送到 userspace,這邊要記錄
- 資料設定完畢後, 呼叫 ovs_dp_upcall來處理
1 | int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, |
- 檢查 porrid(port number)是否正常
- 取得該 datapath的index
- 根據有使用 gso的話,就會特別處理,因為封包的長度比較大,會透過多次的 queue_userspace_packet來處理支援 gso的封包。
- 如果發生error,意味者這個封包就不會有人處理了,因此把lost的值增加
queue_userspace_packet
1 |
|
- 這邊產生 generic netlink 然後把資料設定完畢後,就送出到 userspace
- genlmsg_系列尚未完全瞭解,待補充
個人資訊
我目前於 Hiskio 平台上面有開設 Kubernetes 相關課程,歡迎有興趣的人參考並分享,裡面有我從底層到實戰中對於 Kubernetes 的各種想法
線上課程詳細資訊: https://course.hwchiu.com/
另外,歡迎按讚加入我個人的粉絲專頁,裡面會定期分享各式各樣的文章,有的是翻譯文章,也有部分是原創文章,主要會聚焦於 CNCF 領域
https://www.facebook.com/technologynoteniu
如果有使用 Telegram 的也可以訂閱下列頻道來,裡面我會定期推播通知各類文章
https://t.me/technologynote
你的捐款將給予我文章成長的動力