Updated the dpif_nl_exec_monitor.py utility to also optionally monitor the other DPIF netlink operations, i.e., DPIF_OP_FLOW_PUT, DPIF_OP_FLOW_DEL and DPIF_OP_FLOW_GET
Signed-off-by: Eelco Chaudron <[email protected]> --- Documentation/topics/usdt-probes.rst | 7 +- utilities/automake.mk | 6 +- ..._exec_monitor.py => dpif_op_nl_monitor.py} | 279 +++++++++++++----- 3 files changed, 207 insertions(+), 85 deletions(-) rename utilities/usdt-scripts/{dpif_nl_exec_monitor.py => dpif_op_nl_monitor.py} (72%) diff --git a/Documentation/topics/usdt-probes.rst b/Documentation/topics/usdt-probes.rst index b9a6c54b2..93bfaec12 100644 --- a/Documentation/topics/usdt-probes.rst +++ b/Documentation/topics/usdt-probes.rst @@ -239,7 +239,7 @@ DPIF_OP_FLOW_DEL operation as part of the dpif ``operate()`` callback. **Script references**: -- *None* +- ``utilities/usdt-scripts/dpif_op_nl_monitor.py`` dpif_netlink_operate\_\_:op_flow_execute @@ -260,7 +260,7 @@ DPIF_OP_FLOW_EXECUTE operation as part of the dpif ``operate()`` callback. **Script references**: -- ``utilities/usdt-scripts/dpif_nl_exec_monitor.py`` +- ``utilities/usdt-scripts/dpif_op_nl_monitor.py`` - ``utilities/usdt-scripts/upcall_cost.py`` @@ -281,7 +281,7 @@ DPIF_OP_FLOW_GET operation as part of the dpif ``operate()`` callback. **Script references**: -- *None* +- ``utilities/usdt-scripts/dpif_op_nl_monitor.py`` dpif_netlink_operate\_\_:op_flow_put @@ -301,6 +301,7 @@ DPIF_OP_FLOW_PUT operation as part of the dpif ``operate()`` callback. **Script references**: +- ``utilities/usdt-scripts/dpif_op_nl_monitor.py`` - ``utilities/usdt-scripts/upcall_cost.py`` diff --git a/utilities/automake.mk b/utilities/automake.mk index 146b8c37f..acc1af4e0 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -22,7 +22,7 @@ scripts_SCRIPTS += \ scripts_DATA += utilities/ovs-lib usdt_SCRIPTS += \ utilities/usdt-scripts/bridge_loop.bt \ - utilities/usdt-scripts/dpif_nl_exec_monitor.py \ + utilities/usdt-scripts/dpif_op_nl_monitor.py \ utilities/usdt-scripts/flow_reval_monitor.py \ utilities/usdt-scripts/kernel_delay.py \ utilities/usdt-scripts/kernel_delay.rst \ @@ -72,7 +72,7 @@ EXTRA_DIST += \ utilities/docker/debian/Dockerfile \ utilities/docker/debian/build-kernel-modules.sh \ utilities/usdt-scripts/bridge_loop.bt \ - utilities/usdt-scripts/dpif_nl_exec_monitor.py \ + utilities/usdt-scripts/dpif_op_nl_monitor.py \ utilities/usdt-scripts/flow_reval_monitor.py \ utilities/usdt-scripts/kernel_delay.py \ utilities/usdt-scripts/kernel_delay.rst \ @@ -147,7 +147,7 @@ FLAKE8_PYFILES += utilities/ovs-pcap.in \ utilities/ovs-check-dead-ifs.in \ utilities/ovs-tcpdump.in \ utilities/ovs-pipegen.py \ - utilities/usdt-scripts/dpif_nl_exec_monitor.py \ + utilities/usdt-scripts/dpif_op_nl_monitor.py \ utilities/usdt-scripts/flow_reval_monitor.py \ utilities/usdt-scripts/upcall_monitor.py \ utilities/usdt-scripts/upcall_cost.py diff --git a/utilities/usdt-scripts/dpif_nl_exec_monitor.py b/utilities/usdt-scripts/dpif_op_nl_monitor.py similarity index 72% rename from utilities/usdt-scripts/dpif_nl_exec_monitor.py rename to utilities/usdt-scripts/dpif_op_nl_monitor.py index a5cf1a35a..c0e0a9fae 100755 --- a/utilities/usdt-scripts/dpif_nl_exec_monitor.py +++ b/utilities/usdt-scripts/dpif_op_nl_monitor.py @@ -19,7 +19,8 @@ # dpif_nl_exec_monitor.py uses the dpif_netlink_operate__:op_flow_execute USDT # probe to receive all DPIF_OP_EXECUTE operations that are queued for # transmission over the netlink socket. It will do some basic decoding, and if -# requested a packet dump. +# requested a packet dump. Note that there are also options to obtain +# additional information for the DPIF_OP_FLOW_* operations. # # Here is an example: # @@ -75,31 +76,9 @@ # seq = 0xc # # The example above dumps the full netlink and packet decode. However options -# exist to disable this. Here is the full list of supported options: -# -# usage: dpif_nl_exec_monitor.py [-h] [--buffer-page-count NUMBER] [-D [DEBUG]] -# [-d {none,hex,decode}] [-n {none,hex,nlraw}] -# [-p VSWITCHD_PID] [-s [64-2048]] -# [-w PCAP_FILE] -# -# optional arguments: -# -h, --help show this help message and exit -# --buffer-page-count NUMBER -# Number of BPF ring buffer pages, default 1024 -# -D [DEBUG], --debug [DEBUG] -# Enable eBPF debugging -# -d {none,hex,decode}, --packet-decode {none,hex,decode} -# Display packet content in selected mode, default none -# -n {none,hex,nlraw}, --nlmsg-decode {none,hex,nlraw} -# Display netlink message content in selected mode, -# default nlraw -# -p VSWITCHD_PID, --pid VSWITCHD_PID -# ovs-vswitch's PID -# -s [64-2048], --nlmsg-size [64-2048] -# Set maximum netlink message size to capture, default -# 512 -# -w PCAP_FILE, --pcap PCAP_FILE -# Write upcall packets to specified pcap file +# exist to disable this. For a complete list of options, please use the +# '--help' or '-h' argument. +# from bcc import BPF, USDT, USDTException from os.path import exists @@ -142,12 +121,10 @@ struct ofpbuf { BPF_RINGBUF_OUTPUT(events, <BUFFER_PAGE_CNT>); BPF_TABLE("percpu_array", uint32_t, uint64_t, dropcnt, 1); -int trace__op_flow_execute(struct pt_regs *ctx) { - struct ofpbuf nlbuf; +static int trace_event(struct ofpbuf *nlbuf) +{ uint32_t size; - bpf_usdt_readarg_p(5, ctx, &nlbuf, sizeof(nlbuf)); - struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t)); if (!event) { uint32_t type = 0; @@ -163,17 +140,51 @@ int trace__op_flow_execute(struct pt_regs *ctx) { event->pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&event->comm, sizeof(event->comm)); - event->nl_size = nlbuf.size; + event->nl_size = nlbuf->size; if (event->nl_size > MAX_NLMSG) size = MAX_NLMSG; else size = event->nl_size; - bpf_probe_read(&event->nl_msg, size, nlbuf.data); + bpf_probe_read(&event->nl_msg, size, nlbuf->data); events.ringbuf_submit(event, 0); return 0; +} + +int trace__op_execute(struct pt_regs *ctx) { + struct ofpbuf nlbuf; + + bpf_usdt_readarg_p(5, ctx, &nlbuf, sizeof(nlbuf)); + return trace_event(&nlbuf); +}; + +#if <ENABLE_OP_FLOW_PUT> +int trace__op_flow_put(struct pt_regs *ctx) { + struct ofpbuf nlbuf; + + bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf)); + return trace_event(&nlbuf); +}; +#endif + +#if <ENABLE_OP_FLOW_DEL> +int trace__op_flow_del(struct pt_regs *ctx) { + struct ofpbuf nlbuf; + + bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf)); + return trace_event(&nlbuf); }; +#endif + +#if <ENABLE_OP_FLOW_GET> +int trace__op_flow_get(struct pt_regs *ctx) { + struct ofpbuf nlbuf; + + bpf_usdt_readarg_p(4, ctx, &nlbuf, sizeof(nlbuf)); + return trace_event(&nlbuf); +}; +#endif """ @@ -182,21 +193,24 @@ int trace__op_flow_execute(struct pt_regs *ctx) { # def print_event(ctx, data, size): event = b["events"].event(data) - print("{:<18.9f} {:<4} {:<16} {:<10} {:<10}". + + if event.nl_size < options.nlmsg_size: + nl_size = event.nl_size + else: + nl_size = options.nlmsg_size + + print("{:<18.9f} {:<4} {:<16} {:<10} {:<10} {}". format(event.ts / 1000000000, event.cpu, event.comm.decode("utf-8"), event.pid, - event.nl_size)) + event.nl_size, + get_ovs_dpif_op_str(get_cmd_type_from_nlm( + bytes(event.nl_msg)[:nl_size])))) # # Dumping the netlink message data if requested. # - if event.nl_size < options.nlmsg_size: - nl_size = event.nl_size - else: - nl_size = options.nlmsg_size - if options.nlmsg_decode == "hex": # # Abuse scapy's hex dump to dump flow key @@ -306,6 +320,19 @@ def decode_nlm_tlvs(tlvs, header=None, indent=4, dump=True, return result +# +# get_cmd_type_from_nlm() +# +def get_cmd_type_from_nlm(nlm): + # The netlink message consists of at least a 'struct nlmsghdr' (16-bytes) + # followed by a 'struct genlmsghdr'. The first byte of the genlmsghdr + # structure contains the command. Which is what we will extract here. + if len(nlm) < 17: + return -1 + + return nlm[16] + + # # decode_nlm() # @@ -325,9 +352,11 @@ def decode_nlm(msg, indent=4, dump=True): # # Decode 'struct genlmsghdr' # + cmd, version, reserved = struct.unpack("=BBH", msg[:4]) + if dump: - print("{}genlmsghdr: cmd = {}, version = {}, reserver = {}".format( - " " * indent, *struct.unpack("=BBH", msg[:4]))) + print("{}genlmsghdr: cmd = {}, version = {}, reserved = {}".format( + " " * indent, get_ovs_dpif_op_str(cmd), version, reserved)) msg = msg[4:] @@ -343,42 +372,94 @@ def decode_nlm(msg, indent=4, dump=True): # # Decode TLVs # - nl_attr_tree = { - "OVS_PACKET_ATTR_KEY": { - "header": "> Decode OVS_KEY_ATTR_* TLVs:", - "indent": 4, - "attr_str_func": get_ovs_key_attr_str, - "decode_tree": None, - }, - "OVS_PACKET_ATTR_ACTIONS": { - "header": "> Decode OVS_ACTION_ATTR_* TLVs:", - "indent": 4, - "attr_str_func": get_ovs_action_attr_str, - "decode_tree": { - "OVS_ACTION_ATTR_SET": { - "header": "> Decode OVS_KEY_ATTR_* TLVs:", - "indent": 4, - "attr_str_func": get_ovs_key_attr_str, - "decode_tree": { - "OVS_KEY_ATTR_TUNNEL": { - "header": "> Decode OVS_TUNNEL_KEY_ATTR_* TLVs:", - "indent": 4, - "attr_str_func": get_ovs_tunnel_key_attr_str, - "decode_tree": None, - }, + nl_key_attr = { + "header": "> Decode OVS_KEY_ATTR_* TLVs:", + "indent": 4, + "attr_str_func": get_ovs_key_attr_str, + "decode_tree": { + "OVS_KEY_ATTR_ENCAP": { + "header": "> Decode OVS_KEY_ATTR_* TLVs:", + "indent": 4, + "attr_str_func": get_ovs_key_attr_str, + "decode_tree": { + "OVS_KEY_ATTR_ENCAP": { + "header": "> Decode OVS_KEY_ATTR_* TLVs:", + "indent": 4, + "attr_str_func": get_ovs_key_attr_str, + "decode_tree": None, + }, + }, + }, + } + } + + nl_action_attr = { + "header": "> Decode OVS_ACTION_ATTR_* TLVs:", + "indent": 4, + "attr_str_func": get_ovs_action_attr_str, + "decode_tree": { + "OVS_ACTION_ATTR_SET": { + "header": "> Decode OVS_KEY_ATTR_* TLVs:", + "indent": 4, + "attr_str_func": get_ovs_key_attr_str, + "decode_tree": { + "OVS_KEY_ATTR_TUNNEL": { + "header": "> Decode OVS_TUNNEL_KEY_ATTR_* TLVs:", + "indent": 4, + "attr_str_func": get_ovs_tunnel_key_attr_str, + "decode_tree": None, }, }, }, }, } - result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump, - header="> Decode OVS_PACKET_ATTR_* TLVs:", - attr_to_str_func=get_ovs_pkt_attr_str, - decode_tree=nl_attr_tree) + nl_attr_tree_exec = { + "OVS_PACKET_ATTR_KEY": nl_key_attr, + "OVS_PACKET_ATTR_ACTIONS": nl_action_attr, + } + + nl_attr_tree_put = { + "OVS_FLOW_ATTR_KEY": nl_key_attr, + "OVS_FLOW_ATTR_MASK": nl_key_attr, + "OVS_FLOW_ATTR_ACTIONS": nl_action_attr, + } + + if get_ovs_dpif_op_str(cmd) == "DPIF_OP_EXECUTE": + result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump, + header="> Decode OVS_PACKET_ATTR_* TLVs:", + attr_to_str_func=get_ovs_pkt_attr_str, + decode_tree=nl_attr_tree_exec) + else: + result = decode_nlm_tlvs(msg, indent=indent + 2, dump=dump, + header="> Decode OVS_FLOW_ATTR_* TLVs:", + attr_to_str_func=get_ovs_flow_attr_str, + decode_tree=nl_attr_tree_put) return result +# +# get_ovs_flow_attr_str() +# +def get_ovs_flow_attr_str(attr): + ovs_flow_attr = ["OVS_FLOW_ATTR_UNSPEC", + "OVS_FLOW_ATTR_KEY", + "OVS_FLOW_ATTR_ACTIONS", + "OVS_FLOW_ATTR_STATS", + "OVS_FLOW_ATTR_TCP_FLAGS", + "OVS_FLOW_ATTR_USED", + "OVS_FLOW_ATTR_CLEAR", + "OVS_FLOW_ATTR_MASK", + "OVS_FLOW_ATTR_PROBE", + "OVS_FLOW_ATTR_UFID", + "OVS_FLOW_ATTR_UFID_FLAGS", + "OVS_FLOW_ATTR_PAD"] + if attr < 0 or attr >= len(ovs_flow_attr): + return "<UNKNOWN:{}>".format(attr) + + return ovs_flow_attr[attr] + + # # get_ovs_pkt_attr_str() # @@ -507,6 +588,22 @@ def get_ovs_tunnel_key_attr_str(attr): return ovs_tunnel_key_attr[attr] +# +# get_ovs_dpif_op_str() +# +def get_ovs_dpif_op_str(op): + ovs_dpif_ops = ["DPIF_OP_UNSPEC", + "DPIF_OP_FLOW_PUT", + "DPIF_OP_FLOW_DEL", + "DPIF_OP_EXECUTE", + "DPIF_OP_FLOW_GET"] + + if op < 0 or op >= len(ovs_dpif_ops): + return "<UNKNOWN:{}>".format(op) + + return ovs_dpif_ops[op] + + # # buffer_size_type() # @@ -566,8 +663,17 @@ def main(): help="Set maximum netlink message size to capture, " "default 512", type=buffer_size_type, default=512, metavar="[64-2048]") + parser.add_argument("--trace-del-op", + help="Monitor DPIF_OP_FLOW_DEL messages", + action="store_true") + parser.add_argument("--trace-get-op", + help="Monitor DPIF_OP_FLOW_GET messages", + action="store_true") + parser.add_argument("--trace-put-op", + help="Monitor DPIF_OP_FLOW_PUT messages", + action="store_true") parser.add_argument("-w", "--pcap", metavar="PCAP_FILE", - help="Write upcall packets to specified pcap file", + help="Write execute packets to specified pcap file", type=str, default=None) options = parser.parse_args() @@ -606,13 +712,21 @@ def main(): u = USDT(pid=int(options.pid)) try: u.enable_probe(probe="dpif_netlink_operate__:op_flow_execute", - fn_name="trace__op_flow_execute") + fn_name="trace__op_execute") + if options.trace_put_op: + u.enable_probe(probe="dpif_netlink_operate__:op_flow_put", + fn_name="trace__op_flow_put") + if options.trace_del_op: + u.enable_probe(probe="dpif_netlink_operate__:op_flow_del", + fn_name="trace__op_flow_del") + if options.trace_get_op: + u.enable_probe(probe="dpif_netlink_operate__:op_flow_get", + fn_name="trace__op_flow_get") except USDTException as e: - print("ERROR: {}" - "ovs-vswitchd!".format( - (re.sub("^", " " * 7, str(e), flags=re.MULTILINE)).strip(). - replace("--with-dtrace or --enable-dtrace", - "--enable-usdt-probes"))) + print("ERROR: {}".format( + (re.sub("^", " " * 7, str(e), flags=re.MULTILINE)).strip(). + replace("--with-dtrace or --enable-dtrace", + "--enable-usdt-probes"))) sys.exit(-1) # @@ -627,15 +741,22 @@ def main(): source = source.replace("<BUFFER_PAGE_CNT>", str(options.buffer_page_count)) + source = source.replace("<ENABLE_OP_FLOW_DEL>", + "1" if options.trace_del_op else "0") + source = source.replace("<ENABLE_OP_FLOW_GET>", + "1" if options.trace_get_op else "0") + source = source.replace("<ENABLE_OP_FLOW_PUT>", + "1" if options.trace_put_op else "0") + b = BPF(text=source, usdt_contexts=[u], debug=options.debug) # # Print header # - print("Display DPIF_OP_EXECUTE operations being queued for transmission " - "onto the netlink socket.") - print("{:<18} {:<4} {:<16} {:<10} {:<10}".format( - "TIME", "CPU", "COMM", "PID", "NL_SIZE")) + print("Display DPIF operations being queued for transmission onto the " + "netlink socket.") + print("{:<18} {:<4} {:<16} {:<10} {:<10} {}".format( + "TIME", "CPU", "COMM", "PID", "NL_SIZE", "DPIF_OPERATION")) # # Dump out all events -- 2.45.2 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
