On 21 Aug 2024, at 21:48, Ilya Maximets wrote:

> On 8/16/24 13:02, Eelco Chaudron wrote:
>> 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
>
> Should the script be renamed then?

I was in doubt, dpif_nl_op_monitor.py was my idea. If you feel like we should, 
let me know and I do it in next rev.

> BTW, get_ovs_action_attr_str() is outdated.  Should be a separate fix though.

Good catch, I will add a patch.

>
>>
>> Signed-off-by: Eelco Chaudron <echau...@redhat.com>
>> ---
>>  .../usdt-scripts/dpif_nl_exec_monitor.py      | 276 +++++++++++++-----
>>  1 file changed, 197 insertions(+), 79 deletions(-)
>>
>> diff --git a/utilities/usdt-scripts/dpif_nl_exec_monitor.py 
>> b/utilities/usdt-scripts/dpif_nl_exec_monitor.py
>> index 0a9ff8123..b860d4359 100755
>> --- a/utilities/usdt-scripts/dpif_nl_exec_monitor.py
>> +++ b/utilities/usdt-scripts/dpif_nl_exec_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,16 @@ def decode_nlm_tlvs(tlvs, header=None, indent=4, 
>> dump=True,
>>      return result
>>
>>
>> +#
>> +# get_op_type_from_nlm()
>
> Copy-paste error?

Nope, changed the name, will fix.

>> +#
>> +def get_cmd_type_from_nlm(nlm):
>> +    if len(nlm) < 17:
>> +        return -1
>> +
>> +    return struct.unpack("=B", nlm[16:17])[0]
>
> Lots of magic numbers here, can we use some enum/structure?
> Or at least, exaplin what is going on here in the comment.

ACK, will do.

>> +
>> +
>>  #
>>  # decode_nlm()
>>  #
>> @@ -325,9 +349,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 +369,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 cmd == 3:
>
> Magic number.  Maybe use get_ovs_dpif_op_str() and compare the string?

Good idea.

>> +        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()
>>  #
>> @@ -505,6 +583,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()
>>  #
>> @@ -564,8 +658,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()
>> @@ -604,13 +707,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)
>>
>>      #
>> @@ -625,15 +736,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

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to