Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hack Dive Deeper into VPP Internals

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Hiroki SHIROKURA Hiroki SHIROKURA
September 19, 2019

Hack Dive Deeper into VPP Internals

下記イベントの発表資料.
https://vppug.connpass.com/event/143602/

スライド内部のデモ動画のリンク
https://www.youtube.com/watch?v=VHbt88mXgaQ&feature=youtu.be

概要:
VPPはVPPInfra,VLIB,VNET,Pluginと階層構造を持ち,それらを適切に組み合わせたり変更することが可能である. 本発表ではそれらの内部構造を紹介し, 更にVPPをコードレベルでHackするときのTIPを具体例を含み説明する. より具体的にはVPPをベースに独自のパケット処理機能を追加開発, VPPをLinuxやFRRouting等と連携させ実際のルータとして運用させるためのPlugin開発にフォーカスして議論を行う.

本スライドは個人の見解であり所属する会社の意見を代表するものではなく,
また技術に関してのみ議論を行なっている.

Avatar for Hiroki SHIROKURA

Hiroki SHIROKURA

September 19, 2019
Tweet

More Decks by Hiroki SHIROKURA

Other Decks in Technology

Transcript

  1. Hack/Dive Deeper into VPP Internals VPP User Group Volunteer Association

    #2 / 2019.09.19 Hiroki Shirokura Software Engineer, LINE corp @slankdev <[email protected]>
  2. VPPで構成するNOSを構成したい時 Demonstration slankdev/routerd; A Network OS using only Software •

    VPPのC-plane拡張足りなくない? ◦ それぞれのユーザの導入箇所に対して追加開発の必要性がある. ◦ JunosとかIOS-XRとかみたいに普通に使えるわけではない. • netlinkを見ないといけないがユーザによって見る内容が違いすぎる. ◦ VRFをやりたい人, VXLANをやりたい人, BGPだけでいい人. ◦ switchdとか fabricd とか kamueeとか. みんなで別々に作るやつ. ◦ vppsb/routerはてんこ盛りすぎる. 結局追従もできてない? https://wiki.fd.io/view/VPP_Sandbox/router • そのため私は slankdev/routerd というのを作ってる. ◦ 2つで構成される. ◦ cplane-netdev → VPPのplugin ◦ routerd → cumulusでいうswitchd
  3. VPP NOS を BGP Routerとして動かしてみよう Demonstration slankdev/routerd; A Network OS

    using only Software VPP w/ cplane_netdev, Routerd, FRR, Linux でNOS likeなものを構成 VPPはhost-ifでveth0,veth1を使う. (別にtapだろうが, vhost-userだろうが物理NICだろうがOK)
  4. 6 復習: Netlink理解しましょう Demonstration slankdev/routerd; A Network OS using only

    Software slankdev/routerdにはnetlinkをparseしたりデバッグするツールがいくつか含まれています. 例えばsrv6のnetlinkが知りたかったら.... これを読み取ってVPPにAPI経由で sr localsid address <sid> behavior end.dx4 … 的なことをするだけ. VPPには何も変更無しでいける. 素敵!
  5. では今日の話の流れ Hack/Dive Deeper into VPP Internals VPP Internal for dummies

    1 拡張の具体例に合わせてど のように開発するのかをコー ドベースで説明します 2 vDataplane Issues 私が思うVPPのようなDPDK データプレーンの課題等に関 して説明します 今日話せないこと • VPPの概要など • 性能に関して • FDioの意思や, 弊社との関連等 (この内容は所属する会社とは関係ないです.私の趣味です)
  6. 9 基本概念の復習 VPP Internal for Dummies https://fdio-vpp.readthedocs.io/en/latest/gettingstarted/developers/softwarearchitecture.html • VPP-Infra: memory

    mgmt, vectors, rings, hashing, etc... • VLIB: cli, buffer, thread, graph-node, etc... • VNET: device-input, layer-2/3/4 protocols, session, etc... • Plugins: SNAT, SRv6-AM, tap-inject, PPPoE, etc...
  7. vlibの知っておくべき用語等 VPP internal for Dummies だいたい以下を覚えておけば問題なかった • パケットのベクター ◦ ◦

    ◦ ◦ ◦ • パケットの構造体 ◦ データオフセット 番号 パケットトレース情報等を含む パケット一つの情報を持つ構造体 ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ → 次のノードに対してパケットを投げる • パケット処理ノード モードが複数ある ◦ 実行可能になった場合のみ実行される ◦ ノードの実行後に頻繁に実行される ◦ すべてのノードタイプの前に実行される ◦ の協調的マルチスレッド 定期的に 可能である必要がある ◦ 大抵の構造に関する説明は にある
  8. 11 Vector Packet Processor たる所以 (vnet single/dual loop) VPP internal

    for Dummies 引用 static uword simulated_ethernet_interface_tx (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { vlib_buffer_t *bufs[VLIB_FRAME_SIZE]: vlib_buffer_t **b = bufs; vlib_get_buffers(vm,vlib_frame_vector_args(frame),bufs,frame->n_vectors); u16 nexts[VLIB_FRAME_SIZE]; u16 *next = nexts; while (n_left_from >= 4) { if (PREDICT_TRUE(n_left_from >= 8)) { vlib_prefetch_buffer_header(b[4],STORE); vlib_prefetch_buffer_header(b[5],STORE); vlib_prefetch_buffer_header(b[6],STORE); vlib_prefetch_buffer_header(b[7],STORE); } do_something_to(b[0]); do_something_to(b[1]); do_something_to(b[2]); do_something_to(b[3]); b += 4; next += 4; n_left_from -= 4; } while (n_left_from > 0) { do_something_to(b[0]); b += 1; next += 1; n_left_from -= 1; } vlib_buffer_enqueue_to_next(vm,node,from,nexts,frame->n_vectors); return frame->n_vectors; } 典型的なグラフノードのパケット処理関数. 2つのwhileループ 同期メモリprefetchの時間差を考慮して, CPU d-cache効率性を高めるためのテクニック. VPP(Vector Packet Processor)と呼ばれる由縁. 実際にはこのように実装しなくてもOK CPUの構造上歴史的に実践的なテクニックとして知 られている. 参考: https://wiki.fd.io/view/VPP/Missing_Prefetches
  9. VPP内部でスレッドを起動する VPP internal for Dummies VPP内部で定期実行されるスレッドを起動する → node-type-processを指定するだけ. uint64_t my_process_fn

    (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f){ for (int i=0;; i++) { vlib_cli_output (vm, "slankdev: %d", i); vlib_process_suspend (vm, 1e0/* secs */ ); } return 0; } VLIB_REGISTER_NODE (test_proc, static) = { .function = my_process_fn, .name = "my-process", .type = VLIB_NODE_TYPE_PROCESS, }; VPPは独自のスレッドスケジューラが動いている. 協調的マルチスレッドプログラミングをする必要がある. vlib_process_suspendでyieldをする. 忘れるとコンテキスト解放をしないので他が固まる. typeはprocess, input, interrupt等複数ある. processにするとmaster threadで実行される. inputにするとそれぞれのworkerで実行される. https://my-vpp-docs.readthedocs.io/en/what-is-vpp/overview/whatisvpp/softwarearc hitecture.html • NODEステートも複数ある. VLIB_NODE_STATE_{POLLING, INTERRUPT, DISABLED} ◦ POLLING: Input node is called each iteration of main loop. This is the default (zero). ◦ INTERRUPT: Input node is called when device signals an interrupt. ◦ DISABLED: Input node is never called. ◦ https://docs.fd.io/vpp/19.08/d0/da8/vlib_2node_8h.html#a602bb12cdadc6f44d47ce93278c2c360
  10. vpp# show ip local Protocols handled by ip4_local ICMP: cplane_netdev-tap-inject-tx

    IGMP: igmp-input TCP: cplane_netdev-tap-inject-tx UDP: cplane_netdev-tap-inject-tx OSPF: cplane_netdev-tap-inject-tx Check Registered nodes 13 パケット処理機能の拡張(1): protocol registration VPP internal for Dummies ethernet_register_input_type(vm,ETHERNET_TYPE_CDP, cdp_input_node.index); ip4_register_protocol(IP_PROTOCOL_GRE,gre_input_node.index); ip6_register_protocol(IP_PROTOCOL_L2TP,l2t_decap_node.index); udp_register_dst_port(vm,UDP_DST_PORT_vxlan,vxlan_input_node.index,1); Register New Protocols vnet_hw_interface_rx_redirect_to_node(vnet_main,hw_if_index,my_graph_node.index); vnet_hw_interface_rx_redirect_to_node(vnet_main,hw_if_index,~0); Register Interface Redirection NOS実装者には重要な機能. VPPは独自のip-stackを持っていて. icmp等ある程度VPP自体が処理できる. NOSの一部として利用するときはkernelに リダイレクトしたいのでprotocol registerを駆使する. 新規ノードを使うのでそのスレッド起動する必要あり. vpp上では show ip local で確認できる slankdev/routerdの場合 自宛ICMP,TCP,UDPをkernelに上げる. 本来この機能はソースコードレベルでやりたくない. 今後は設定可能にしたい.
  11. int override_ethernet_input(vlib_main_t *vm){ vlib_node_t *node = vlib_get_node_by_name(vlib_get_main(), “ethernet-input”); vlib_node_runtime_t *runtime

    = vlib_node_get_runtime(vm, node->index); tap_mirror_main_t *xm = tap_mirror_get_main(); xm->target_fn = runtime->function; //override元を退避 runtime->function = tap_mirror_input_fn; return 0; } overrideの設定をする部分 uint64_t tap_mirror_input_fn(vlib_main_t *vm, vlib_node_runtime_t *node,vlib_frame_t *f){ uint32_t *pkts = vlib_frame_vector_args (f); for (uint32_t i = 0; i < f->n_vectors; ++i) { uint32_t clones[2]; vlib_buffer_clone (vm, pkts[i], clones, 2, VLIB_BUFFER_CLONE_HEAD_SIZE); vlib_buffer_t *b = vlib_get_buffer (vm, clones[2]); vlib_buffer_advance (b, -b->current_data); write(xm->tap_fd,vlib_buffer_get_current(b),vlib_buffer_length_in_chain(vm, b)); vlib_buffer_free (vm, &xm->clones[thread_index][1], 1); } tap_mirror_main_t *xm = tap_mirror_get_main(); return xm->target_fn(vm, node, f); } 14 パケット処理機能の拡張(2): feature arcs VPP internal for Dummies 各ノードの実行される関数はvlib_node_runtimeオブジェクトか ら取得可能, 上書き可能で, これは各ノード名から受け取ることが できる. 以下は任意のノードの関数実行前に独自の関数を挿入する関数. パケットをcloneしてからtap_fdに書き込んで, cloneで追加した 分だけfreeをする. 基本的な考えはDPDKと同様でよいが, clone の仕様が少しだけ違うので注意が必要. この例はinlineで処理しているがこれにより性能ダウンする. 実際のtap-mirrorではvlib_bufferを別threadに渡してからtap 書き込みをおこおなう. この方法なら既存のnodeのコンテキストで動くのでスレッドとか追 加しなくて良い. 実際にoverrideされた場所で呼び出される処理
  12. 15 パケット処理機能の拡張(3): ethernet deviceクラスの追加 VPP internal for Dummies bash$ less

    src/vnet/devices/tap/tap.c …(snip)… ret = ethernet_register_interface (vnm, virtio_device_class.index, vif->dev_instance, vif->mac_addr, &vif->hw_if_index, virtio_eth_flag_change); …(snip)… vppのtap interfaceの作成時のデバイス登録部分 上記はhw_if_indexの登録をしている. その他は基本的な設定関数に hw_if_indexを突っ込んで設定していくだけ. input系の関数はnodeを登録してそこから パケットをもってくる. AF_XDPのやつとかつくるのはここから
  13. 16 複数スレッド間でシグナリングをする方法 VPP internal for Dummies uint64_t data_type = TYPE10;

    uint64_t *data_ptr = &value; vlib_node_t *dest_node = vlib_get_node_by_name(vm,"tap-mirror-redirector"); vlib_process_signal_event_mt(vm,destination_node->index,data_type,data_ptr); nodeのthreadから別のnodeに対してsignalを送る vlib_process_wait_for_event_or_clock(vm, 1.0); uint64_t event_type = vlib_process_get_events (vm, &event_data); switch (event_type) { case TYPE10: printf(“value: %lu\n”, *event_data); break; default: break; } nodeのthreadでsignalを受け付けて処理する VLIB組み込みのsignal機能がある. type(64bit)とvalue(64bit)を送れる. 要するにmessage passingができる. スレッド間の情報授受のチャネルとして活用可能 ex. パケットをcloneして別のthreadに渡す https://my-vpp-docs.readthedocs.io/en/what-is-vpp/overview/whatisvpp/so ftwarearchitecture.html#process-events
  14. 17 開発中に無駄なBuildを省略する VPP internal for Dummies vi src/vnet/CMakeLists.txt → 必要なvnetパッケージをコメントアウト.

    依存関係に注意 git diff src/vnet/CMakeLists.txt | grep "^\-# " -# Layer 2 / LLDP -# Tunnel protocol: l2tpv3 -# DHCP client -# DHCP proxy -# ipv6 segment routing -# mpls segment routing -# IPFIX / netflow v10 -# IPFIX classify code -# DNS proxy, API -# TLS protocol 必要のないvnet等のbuildを無効にする 30s → 17s rm src/plugins/{pppoe,nat,srv6-am,srv6-ad,srv6-ac} → これでもよい vi src/CMakeLists.txt → pluginをコメントアウト 超簡単: 必要のないpluginのbuildを無効にする
  15. 18 デバッグ機能(1): Packet Trace VPP internal for Dummies 受信したパケットにマークをつけてどのノードでどのように処理さ れたかを追跡する機能

    開発時やデバッグ時に活用する ただ 内部の機能に非常に密接になっているため ルータのデ バッグ機能として利用するには少し使いづらいかも そして の のなまえは なので のトレース をするときに なのに って指定する必要があるという DBGvpp# trace add virtio-input 1 DBGvpp# show trace ------------- Start of thread 0 vpp_main ------------- Packet 1 00:00:16:119960: virtio-input virtio: hw_if_index 3 next-index 4 vring 0 len 98 hdr: flags 0x00 gso_type 0x00 hdr_len 0 gso_size 0 csum_start 0 csum_offset 0 num_buffers 1 00:00:16:119984: ethernet-input IP4: aa:02:54:1b:f0:fe -> 02:fe:c9:2b:9d:21 …(snip)… 00:00:16:120055: ip4-icmp-echo-request ICMP: 10.0.0.2 -> 10.0.0.1 tos 0x00, ttl 64, length 84, checksum 0x2eab fragment id 0xf7fb, flags DONT_FRAGMENT ICMP echo_request checksum 0x684c 00:00:16:120065: tap0-output tap0 l2_hdr_offset_valid l3_hdr_offset_valid IP4: 02:fe:c9:2b:9d:21 -> aa:02:54:1b:f0:fe ICMP: 10.0.0.1 -> 10.0.0.2 tos 0x00, ttl 64, length 84, checksum 0xff97 fragment id 0x270f, flags DONT_FRAGMENT ICMP echo_reply checksum 0x704c
  16. 19 デバッグ機能(2): Pcap Trace VPP internal for Dummies 各パケット処理ノードに対して に書き出す

    を発 行できる 関連 の と似ている のほうが先に公開 の は のデバッグ機能として活用でき る の データ等もキャプチャされる にも した結果やっと機能する少 し大きなもの これは運用時というより開発時に役立ちそ うな機能である 運用時は
  17. VPP等のDPDK Dataplaneの課題 (1/2) vDataplane Issues • XDPは素晴らしい発明 ◦ データプレーンのソフトウェアエンジニアにかかる負担が小さい. 簡単にLinux

    Kernelの資産を使える (fib lookup, reuse-kernel-network-stack XDP_PASS) ◦ そのうちXDPとかルータが出てくるかも? VPP/DPDKの危機?そしたらみんなで移行しましょう eBPF/XDP, AF_XDPをよく調査しましょう. • じゃあVPPはいらない子? → そんなことない. 導入例を示そう. (これが一番むずい..?) ◦ コミュニティ貢献により堅牢で安定したデータプレーン (これは本当にすごい) ◦ 実際の運用に対して足りない機能がある. (C-plane vppsb/routerは古いですよね?) ◦ API等の強化が今後の発展に貢献? (govpp, binary-api, etc...) • vppsb/routerは何が行けなかったのか ◦ 彼のやろうとしたことは大きすぎた (tap-injectのみで十分なんや) ◦ netlinkの面倒は単一のdataplaneには酷. 分離しましょう. (多くのNOSはどうだろう)
  18. VPP等のDPDK Dataplaneの課題 (2/2) vDataplane Issues • 仮想化技術との親和性を再確認しよう ◦ DPDKって, VMとかコンテナと相性いいんだっけ?

    ◦ 仮想化ノードでDPDKを動かすなら最高に相性はいい. ▪ Hyper ScalerのサービスはwebだからVMはDPDKを使わない ▪ MobileNW近辺のEPCとかはDPDKとか使うだろうから良さそうな... ▪ 振り返って, Openstack, Kubernetesの基本的な使い方に合わせられる? ◦ L4LBが良い場所の一つ ▪ もっと試さないといけないですよね. ▪ 導入してみましょう! • みんな(僕も) Hardware Offloadingをもっと知ろう. ◦ もっとできることが増えると思いますよ. ◦ VPPを実際に使うときに全然offloadingが使えないことに気づくかもしれません. ◦ Tunneling Inner Header RSS / Segmentation Offload / Checksum Offloading
  19. • golang, pythonのどちらかで書き直す? ◦ netlinkを解析するのがcがやりやすかった... ◦ vish氏のgo-netlinkは拡張が少しめんどくさそうに思ったのでそもそも触ってない. ◦ そもそもpyroute2で十分なんだよな... ◦

    移植したfrrのvtyを手放すのが悲しい (どうでもいい理由) ◦ grpc等のrpcと親和性を高めたい. (c++でgrpc周りを書くのはめんどくさい) • cplane-netdevはFDioに提案して入れてもらいたい ◦ 全部キレイに書き直し ◦ whitebox switch developerの知見を借りたい • 似たようなことをやっている人と協力したい. ◦ https://github.com/FRRouting/frr/wiki/Alternate-forwarding-planes:-VPP ◦ https://static.sched.com/hosted_files/onsna19/73/ONS.NA.2019.VPP_LB_public.pdf → どのVPP version使っているのだろう... slankdev/routerdの今後の展望 vDataplane Issues