9`sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextChinese (Simplified)}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget+/translations/zh_CN/bpf/prog_flow_dissectormodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/zh_TW/bpf/prog_flow_dissectormodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/it_IT/bpf/prog_flow_dissectormodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/ja_JP/bpf/prog_flow_dissectormodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/ko_KR/bpf/prog_flow_dissectormodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/sp_SP/bpf/prog_flow_dissectormodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageEnglishuh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhE/var/lib/git/docbuild/linux/Documentation/bpf/prog_flow_dissector.rsthKubhsection)}(hhh](htitle)}(hBPF_PROG_TYPE_FLOW_DISSECTORh]hBPF_PROG_TYPE_FLOW_DISSECTOR}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhhhKubh)}(hhh](h)}(hOverviewh]hOverview}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhhhKubh paragraph)}(hFlow dissector is a routine that parses metadata out of the packets. It's used in the various places in the networking subsystem (RFS, flow hash, etc).h]hFlow dissector is a routine that parses metadata out of the packets. It’s used in the various places in the networking subsystem (RFS, flow hash, etc).}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK hhhhubh)}(hBPF flow dissector is an attempt to reimplement C-based flow dissector logic in BPF to gain all the benefits of BPF verifier (namely, limits on the number of instructions and tail calls).h]hBPF flow dissector is an attempt to reimplement C-based flow dissector logic in BPF to gain all the benefits of BPF verifier (namely, limits on the number of instructions and tail calls).}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK hhhhubeh}(h]overviewah ]h"]overviewah$]h&]uh1hhhhhhhhKubh)}(hhh](h)}(hAPIh]hAPI}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhKubh)}(hBPF flow dissector programs operate on an ``__sk_buff``. However, only the limited set of fields is allowed: ``data``, ``data_end`` and ``flow_keys``. ``flow_keys`` is ``struct bpf_flow_keys`` and contains flow dissector input and output arguments.h](h*BPF flow dissector programs operate on an }(hjhhhNhNubhliteral)}(h ``__sk_buff``h]h __sk_buff}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh6. However, only the limited set of fields is allowed: }(hjhhhNhNubj)}(h``data``h]hdata}(hj-hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh, }(hjhhhNhNubj)}(h ``data_end``h]hdata_end}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh and }(hjhhhNhNubj)}(h ``flow_keys``h]h flow_keys}(hjQhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh. }(hjhhhNhNubj)}(h ``flow_keys``h]h flow_keys}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh is }(hjhhhNhNubj)}(h``struct bpf_flow_keys``h]hstruct bpf_flow_keys}(hjuhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh8 and contains flow dissector input and output arguments.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubhdefinition_list)}(hhh]hdefinition_list_item)}(hThe inputs are: * ``nhoff`` - initial offset of the networking header * ``thoff`` - initial offset of the transport header, initialized to nhoff * ``n_proto`` - L3 protocol type, parsed out of L2 header * ``flags`` - optional flags h](hterm)}(hThe inputs are:h]hThe inputs are:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubh definition)}(hhh]h bullet_list)}(hhh](h list_item)}(h3``nhoff`` - initial offset of the networking headerh]h)}(hjh](j)}(h ``nhoff``h]hnhoff}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh* - initial offset of the networking header}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hH``thoff`` - initial offset of the transport header, initialized to nhoffh]h)}(hjh](j)}(h ``thoff``h]hthoff}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh? - initial offset of the transport header, initialized to nhoff}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h7``n_proto`` - L3 protocol type, parsed out of L2 headerh]h)}(hjh](j)}(h ``n_proto``h]hn_proto}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh, - L3 protocol type, parsed out of L2 header}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h``flags`` - optional flags h]h)}(h``flags`` - optional flagsh](j)}(h ``flags``h]hflags}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj'ubh - optional flags}(hj'hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj#ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]bullet*uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhNhNubh)}(hFlow dissector BPF program should fill out the rest of the ``struct bpf_flow_keys`` fields. Input arguments ``nhoff/thoff/n_proto`` should be also adjusted accordingly.h](h;Flow dissector BPF program should fill out the rest of the }(hjchhhNhNubj)}(h``struct bpf_flow_keys``h]hstruct bpf_flow_keys}(hjkhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjcubh fields. Input arguments }(hjchhhNhNubj)}(h``nhoff/thoff/n_proto``h]hnhoff/thoff/n_proto}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjcubh% should be also adjusted accordingly.}(hjchhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h}The return code of the BPF program is either BPF_OK to indicate successful dissection, or BPF_DROP to indicate parsing error.h]h}The return code of the BPF program is either BPF_OK to indicate successful dissection, or BPF_DROP to indicate parsing error.}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK#hjhhubeh}(h]apiah ]h"]apiah$]h&]uh1hhhhhhhhKubh)}(hhh](h)}(h__sk_buff->datah]h__sk_buff->data}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhK'ubh)}(h\In the VLAN-less case, this is what the initial state of the BPF flow dissector looks like::h]h[In the VLAN-less case, this is what the initial state of the BPF flow dissector looks like:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK)hjhhubh literal_block)}(h+------+------+------------+-----------+ | DMAC | SMAC | ETHER_TYPE | L3_HEADER | +------+------+------------+-----------+ ^ | +-- flow dissector starts hereh]h+------+------+------------+-----------+ | DMAC | SMAC | ETHER_TYPE | L3_HEADER | +------+------+------------+-----------+ ^ | +-- flow dissector starts here}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhK,hjhhubj)}(hzskb->data + flow_keys->nhoff point to the first byte of L3_HEADER flow_keys->thoff = nhoff flow_keys->n_proto = ETHER_TYPEh]hzskb->data + flow_keys->nhoff point to the first byte of L3_HEADER flow_keys->thoff = nhoff flow_keys->n_proto = ETHER_TYPE}hjsbah}(h]h ]h"]h$]h&]forcehighlight_args}hhlanguagecuh1jhhhK4hjhhubh)}(hLIn case of VLAN, flow dissector can be called with the two different states.h]hLIn case of VLAN, flow dissector can be called with the two different states.}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK:hjhhubh)}(hPre-VLAN parsing::h]hPre-VLAN parsing:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hnskb->data + flow_keys->nhoff point the to first byte of TCI flow_keys->thoff = nhoff flow_keys->n_proto = TPIDh]hnskb->data + flow_keys->nhoff point the to first byte of TCI flow_keys->thoff = nhoff flow_keys->n_proto = TPID}hjsbah}(h]h ]h"]h$]h&]forcehighlight_args}hhjjuh1jhhhKEhjhhubh)}(hPlease note that TPID can be 802.1AD and, hence, BPF program would have to parse VLAN information twice for double tagged packets.h]hPlease note that TPID can be 802.1AD and, hence, BPF program would have to parse VLAN information twice for double tagged packets.}(hj(hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKKhjhhubh)}(hPost-VLAN parsing::h]hPost-VLAN parsing:}(hj6hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKOhjhhubj)}(hX9+------+------+------+-----+-----------+-----------+ | DMAC | SMAC | TPID | TCI |ETHER_TYPE | L3_HEADER | +------+------+------+-----+-----------+-----------+ ^ | +-- flow dissector starts hereh]hX9+------+------+------+-----+-----------+-----------+ | DMAC | SMAC | TPID | TCI |ETHER_TYPE | L3_HEADER | +------+------+------+-----+-----------+-----------+ ^ | +-- flow dissector starts here}hjDsbah}(h]h ]h"]h$]h&]hhuh1jhhhKQhjhhubj)}(hzskb->data + flow_keys->nhoff point the to first byte of L3_HEADER flow_keys->thoff = nhoff flow_keys->n_proto = ETHER_TYPEh]hzskb->data + flow_keys->nhoff point the to first byte of L3_HEADER flow_keys->thoff = nhoff flow_keys->n_proto = ETHER_TYPE}hjRsbah}(h]h ]h"]h$]h&]forcehighlight_args}hhjjuh1jhhhKXhjhhubh)}(hIn this case VLAN information has been processed before the flow dissector and BPF flow dissector is not required to handle it.h]hIn this case VLAN information has been processed before the flow dissector and BPF flow dissector is not required to handle it.}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK^hjhhubh)}(hX=The takeaway here is as follows: BPF flow dissector program can be called with the optional VLAN header and should gracefully handle both cases: when single or double VLAN is present and when it is not present. The same program can be called for both cases and would have to be written carefully to handle both cases.h]hX=The takeaway here is as follows: BPF flow dissector program can be called with the optional VLAN header and should gracefully handle both cases: when single or double VLAN is present and when it is not present. The same program can be called for both cases and would have to be written carefully to handle both cases.}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKbhjhhubeh}(h] sk-buff-dataah ]h"]__sk_buff->dataah$]h&]uh1hhhhhhhhK'ubh)}(hhh](h)}(hFlagsh]hFlags}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhKjubh)}(hM``flow_keys->flags`` might contain optional input flags that work as follows:h](j)}(h``flow_keys->flags``h]hflow_keys->flags}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh9 might contain optional input flags that work as follows:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKlhjhhubj)}(hhh](j)}(hX!``BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG`` - tells BPF flow dissector to continue parsing first fragment; the default expected behavior is that flow dissector returns as soon as it finds out that the packet is fragmented; used by ``eth_get_headlen`` to estimate length of all headers for GRO.h]h)}(hX!``BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG`` - tells BPF flow dissector to continue parsing first fragment; the default expected behavior is that flow dissector returns as soon as it finds out that the packet is fragmented; used by ``eth_get_headlen`` to estimate length of all headers for GRO.h](j)}(h'``BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG``h]h#BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh - tells BPF flow dissector to continue parsing first fragment; the default expected behavior is that flow dissector returns as soon as it finds out that the packet is fragmented; used by }(hjhhhNhNubj)}(h``eth_get_headlen``h]heth_get_headlen}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh+ to estimate length of all headers for GRO.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKnhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h``BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL`` - tells BPF flow dissector to stop parsing as soon as it reaches IPv6 flow label; used by ``___skb_get_hash`` to get flow hash.h]h)}(h``BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL`` - tells BPF flow dissector to stop parsing as soon as it reaches IPv6 flow label; used by ``___skb_get_hash`` to get flow hash.h](j)}(h+``BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL``h]h'BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh[ - tells BPF flow dissector to stop parsing as soon as it reaches IPv6 flow label; used by }(hjhhhNhNubj)}(h``___skb_get_hash``h]h___skb_get_hash}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh to get flow hash.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKrhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h``BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP`` - tells BPF flow dissector to stop parsing as soon as it reaches encapsulated headers; used by routing infrastructure. h]h)}(h``BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP`` - tells BPF flow dissector to stop parsing as soon as it reaches encapsulated headers; used by routing infrastructure.h](j)}(h&``BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP``h]h"BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj+ubhw - tells BPF flow dissector to stop parsing as soon as it reaches encapsulated headers; used by routing infrastructure.}(hj+hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKuhj'ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]jOjPuh1jhhhKnhjhhubeh}(h]flagsah ]h"]flagsah$]h&]uh1hhhhhhhhKjubh)}(hhh](h)}(hReference Implementationh]hReference Implementation}(hj^hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj[hhhhhK{ubh)}(hSee ``tools/testing/selftests/bpf/progs/bpf_flow.c`` for the reference implementation and ``tools/testing/selftests/bpf/flow_dissector_load.[hc]`` for the loader. bpftool can be used to load BPF flow dissector program as well.h](hSee }(hjlhhhNhNubj)}(h0``tools/testing/selftests/bpf/progs/bpf_flow.c``h]h,tools/testing/selftests/bpf/progs/bpf_flow.c}(hjthhhNhNubah}(h]h ]h"]h$]h&]uh1jhjlubh& for the reference implementation and }(hjlhhhNhNubj)}(h8``tools/testing/selftests/bpf/flow_dissector_load.[hc]``h]h4tools/testing/selftests/bpf/flow_dissector_load.[hc]}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjlubhP for the loader. bpftool can be used to load BPF flow dissector program as well.}(hjlhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK}hj[hhubj)}(hhh]j)}(hXThe reference implementation is organized as follows: * ``jmp_table`` map that contains sub-programs for each supported L3 protocol * ``_dissect`` routine - entry point; it does input ``n_proto`` parsing and does ``bpf_tail_call`` to the appropriate L3 handler h](j)}(h5The reference implementation is organized as follows:h]h5The reference implementation is organized as follows:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKhjubj)}(hhh]j)}(hhh](j)}(hK``jmp_table`` map that contains sub-programs for each supported L3 protocolh]h)}(hjh](j)}(h ``jmp_table``h]h jmp_table}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh> map that contains sub-programs for each supported L3 protocol}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h``_dissect`` routine - entry point; it does input ``n_proto`` parsing and does ``bpf_tail_call`` to the appropriate L3 handler h]h)}(h~``_dissect`` routine - entry point; it does input ``n_proto`` parsing and does ``bpf_tail_call`` to the appropriate L3 handlerh](j)}(h ``_dissect``h]h_dissect}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh& routine - entry point; it does input }(hjhhhNhNubj)}(h ``n_proto``h]hn_proto}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh parsing and does }(hjhhhNhNubj)}(h``bpf_tail_call``h]h bpf_tail_call}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh to the appropriate L3 handler}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]jOjPuh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhj[hhhNhNubh)}(hSince BPF at this point doesn't support looping (or any jumping back), jmp_table is used instead to handle multiple levels of encapsulation (and IPv6 options).h]hSince BPF at this point doesn’t support looping (or any jumping back), jmp_table is used instead to handle multiple levels of encapsulation (and IPv6 options).}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj[hhubeh}(h]reference-implementationah ]h"]reference implementationah$]h&]uh1hhhhhhhhK{ubh)}(hhh](h)}(hCurrent Limitationsh]hCurrent Limitations}(hjYhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjVhhhhhKubh)}(hX4BPF flow dissector doesn't support exporting all the metadata that in-kernel C-based implementation can export. Notable example is single VLAN (802.1Q) and double VLAN (802.1AD) tags. Please refer to the ``struct bpf_flow_keys`` for a set of information that's currently can be exported from the BPF context.h](hBPF flow dissector doesn’t support exporting all the metadata that in-kernel C-based implementation can export. Notable example is single VLAN (802.1Q) and double VLAN (802.1AD) tags. Please refer to the }(hjghhhNhNubj)}(h``struct bpf_flow_keys``h]hstruct bpf_flow_keys}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1jhjgubhR for a set of information that’s currently can be exported from the BPF context.}(hjghhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjVhhubh)}(hWhen BPF flow dissector is attached to the root network namespace (machine-wide policy), users can't override it in their child network namespaces.h]hWhen BPF flow dissector is attached to the root network namespace (machine-wide policy), users can’t override it in their child network namespaces.}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjVhhubeh}(h]current-limitationsah ]h"]current limitationsah$]h&]uh1hhhhhhhhKubeh}(h]bpf-prog-type-flow-dissectorah ]h"]bpf_prog_type_flow_dissectorah$]h&]uh1hhhhhhhhKubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(hN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerjerror_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}nameids}(jjhhjjjjjXjUjSjPjju nametypes}(jhjjjXjSjuh}(jhhhjjjjjUjjPj[jjVu footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}Rparse_messages]transform_messages] transformerN include_log] decorationNhhub.