Slide 1

Slide 1 text

Hack/Dive Deeper into VPP Internals VPP User Group Volunteer Association #2 / 2019.09.19 Hiroki Shirokura Software Engineer, LINE corp @slankdev

Slide 2

Slide 2 text

皆さん どんな用途で 使いますか として使えたら嬉しいですよね とか活用してさ の最新機能とかとも連携してさ 2

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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)

Slide 5

Slide 5 text

の 5

Slide 6

Slide 6 text

6 復習: Netlink理解しましょう Demonstration slankdev/routerd; A Network OS using only Software slankdev/routerdにはnetlinkをparseしたりデバッグするツールがいくつか含まれています. 例えばsrv6のnetlinkが知りたかったら.... これを読み取ってVPPにAPI経由で sr localsid address behavior end.dx4 … 的なことをするだけ. VPPには何も変更無しでいける. 素敵!

Slide 7

Slide 7 text

として使うにはいくつか開発しないとだめで すよね じゃあいろんな拡張を に施すにはどうした らいいだろうか それを今日は話します 以降のスライドは読むためのスライドです 7

Slide 8

Slide 8 text

では今日の話の流れ Hack/Dive Deeper into VPP Internals VPP Internal for dummies 1 拡張の具体例に合わせてど のように開発するのかをコー ドベースで説明します 2 vDataplane Issues 私が思うVPPのようなDPDK データプレーンの課題等に関 して説明します 今日話せないこと ● VPPの概要など ● 性能に関して ● FDioの意思や, 弊社との関連等 (この内容は所属する会社とは関係ないです.私の趣味です)

Slide 9

Slide 9 text

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...

Slide 10

Slide 10 text

vlibの知っておくべき用語等 VPP internal for Dummies だいたい以下を覚えておけば問題なかった ● パケットのベクター ○ ○ ○ ○ ○ ● パケットの構造体 ○ データオフセット 番号 パケットトレース情報等を含む パケット一つの情報を持つ構造体 ○ ○ ○ ○ ○ ○ ○ ○ → 次のノードに対してパケットを投げる ● パケット処理ノード モードが複数ある ○ 実行可能になった場合のみ実行される ○ ノードの実行後に頻繁に実行される ○ すべてのノードタイプの前に実行される ○ の協調的マルチスレッド 定期的に 可能である必要がある ○ 大抵の構造に関する説明は にある

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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に上げる. 本来この機能はソースコードレベルでやりたくない. 今後は設定可能にしたい.

Slide 14

Slide 14 text

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された場所で呼び出される処理

Slide 15

Slide 15 text

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のやつとかつくるのはここから

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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を無効にする

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

19 デバッグ機能(2): Pcap Trace VPP internal for Dummies 各パケット処理ノードに対して に書き出す を発 行できる 関連 の と似ている のほうが先に公開 の は のデバッグ機能として活用でき る の データ等もキャプチャされる にも した結果やっと機能する少 し大きなもの これは運用時というより開発時に役立ちそ うな機能である 運用時は

Slide 20

Slide 20 text

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はどうだろう)

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

● 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

Slide 23

Slide 23 text

発表と関係ありませんが, いっしょに仕事してくれる人がいたらぜひ LINE株式会社 ITSC Verda室 NW開発チーム https://linecorp.com/ja/career/position/564