On 20 Dec 2021, at 0:48, Paolo Valerio wrote:

> Hi Eelco,
>
> the patch LGTM, just a couple of questions/nits.
>
> Eelco Chaudron <[email protected]> writes:
>
>> Added the dpif_recv:recv_upcall USDT probe, which is used by the
>> included upcall_monitor.py script. This script receives all upcall
>> packets sent by the kernel to ovs-vswitchd. By default, it will
>> show all  upcall events, which looks something like this:
>>
>>  TIME               CPU  COMM      PID      DPIF_NAME          TYPE PKT_LEN 
>> FLOW_KEY_LEN
>>  5952147.003848809  2    handler4  1381158  system@ovs-system  0    98      
>> 132
>>  5952147.003879643  2    handler4  1381158  system@ovs-system  0    70      
>> 160
>>  5952147.003914924  2    handler4  1381158  system@ovs-system  0    98      
>> 152
>>
>> It can also dump the packet and NetLink content, and if required,
>> the packets can also be written to a pcap file.
>>
>> Signed-off-by: Eelco Chaudron <[email protected]>
>> ---
>>  Documentation/topics/usdt-probes.rst     |   26 ++
>>  lib/dpif.c                               |   23 +
>>  utilities/automake.mk                    |    6
>>  utilities/usdt_scripts/upcall_monitor.py |  500 
>> ++++++++++++++++++++++++++++++
>>  4 files changed, 545 insertions(+), 10 deletions(-)
>>  create mode 100755 utilities/usdt_scripts/upcall_monitor.py
>>
>> diff --git a/Documentation/topics/usdt-probes.rst 
>> b/Documentation/topics/usdt-probes.rst
>> index 38acf7d02..1f6446e12 100644
>> --- a/Documentation/topics/usdt-probes.rst
>> +++ b/Documentation/topics/usdt-probes.rst
>> @@ -200,10 +200,36 @@ used naming convention.
>>
>>  Available probes in ``ovs_vswitchd``:
>>
>> +- dpif_recv:recv_upcall
>>  - main:poll_block
>>  - main:run_start
>>
>>
>> +probe dpif_recv:recv_upcall
>> +~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> +
>> +**Description**:
>> +
>> +This probe gets triggered when the datapath independent layer gets notified
>> +that a packet needs to be processed by userspace. This allows the probe to
>> +intercept all packets sent by the kernel to ``ovs-vswitchd``. The
>> +``upcall_monitor.py`` script uses this probe to display and capture all 
>> packets
>> +sent to ``ovs-vswitchd``.
>> +
>> +**Arguments**:
>> +
>> +- *arg0*: ``(struct dpif *)->full_name``
>> +- *arg1*: ``(struct dpif_upcall *)->type``
>> +- *arg2*: ``dp_packet_data((struct dpif_upcall *)->packet)``
>> +- *arg3*: ``dp_packet_size((struct dpif_upcall *)->packet)``
>> +- *arg4*: ``(struct dpif_upcall *)->key``
>> +- *arg5*: ``(struct dpif_upcall *)->key_len``
>> +
>> +**Script references**:
>> +
>> +- ``/utilities/usdt_scripts/upcall_monitor.py``
>> +
>
> same nit for the paths (rel vs abs).

Will fix in v2, also in the next patch :)

>> +
>>  probe main:run_start
>>  ~~~~~~~~~~~~~~~~~~~~
>>
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index 38bcb47cb..ab75b3c17 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -15,7 +15,6 @@
>>   */
>>
>>  #include <config.h>
>> -#include "dpif-provider.h"
>>
>>  #include <ctype.h>
>>  #include <errno.h>
>> @@ -24,22 +23,19 @@
>>  #include <string.h>
>>
>>  #include "coverage.h"
>> -#include "dpctl.h"
>>  #include "dp-packet.h"
>> +#include "dpctl.h"
>>  #include "dpif-netdev.h"
>> -#include "openvswitch/dynamic-string.h"
>> +#include "dpif-provider.h"
>>  #include "flow.h"
>> +#include "netdev-provider.h"
>>  #include "netdev.h"
>>  #include "netlink.h"
>>  #include "odp-execute.h"
>>  #include "odp-util.h"
>> -#include "openvswitch/ofp-print.h"
>> -#include "openvswitch/ofpbuf.h"
>>  #include "packets.h"
>> -#include "openvswitch/poll-loop.h"
>>  #include "route-table.h"
>>  #include "seq.h"
>> -#include "openvswitch/shash.h"
>>  #include "sset.h"
>>  #include "timeval.h"
>>  #include "tnl-neigh-cache.h"
>> @@ -47,9 +43,14 @@
>>  #include "util.h"
>>  #include "uuid.h"
>>  #include "valgrind.h"
>> +#include "openvswitch/dynamic-string.h"
>>  #include "openvswitch/ofp-errors.h"
>> +#include "openvswitch/ofp-print.h"
>> +#include "openvswitch/ofpbuf.h"
>> +#include "openvswitch/poll-loop.h"
>> +#include "openvswitch/shash.h"
>> +#include "openvswitch/usdt_probes.h"
>>  #include "openvswitch/vlog.h"
>> -#include "lib/netdev-provider.h"
>>
>>  VLOG_DEFINE_THIS_MODULE(dpif);
>>
>> @@ -1602,6 +1603,12 @@ dpif_recv(struct dpif *dpif, uint32_t handler_id, 
>> struct dpif_upcall *upcall,
>>      if (dpif->dpif_class->recv) {
>>          error = dpif->dpif_class->recv(dpif, handler_id, upcall, buf);
>>          if (!error) {
>> +            OVS_USDT_PROBE(dpif_recv, recv_upcall, dpif->full_name,
>> +                           upcall->type,
>> +                           dp_packet_data(&upcall->packet),
>> +                           dp_packet_size(&upcall->packet),
>> +                           upcall->key, upcall->key_len);
>> +
>>              dpif_print_packet(dpif, upcall);
>>          } else if (error != EAGAIN) {
>>              log_operation(dpif, "recv", error);
>> diff --git a/utilities/automake.mk b/utilities/automake.mk
>> index 55c7b0022..382f8e789 100644
>> --- a/utilities/automake.mk
>> +++ b/utilities/automake.mk
>> @@ -62,7 +62,8 @@ EXTRA_DIST += \
>>      utilities/docker/create_ovs_db.sh \
>>      utilities/docker/debian/Dockerfile \
>>      utilities/docker/debian/build-kernel-modules.sh \
>> -    utilities/usdt_scripts/bridge_loop.bt
>> +    utilities/usdt_scripts/bridge_loop.bt \
>> +    utilities/usdt_scripts/upcall_monitor.py
>>  MAN_ROOTS += \
>>      utilities/ovs-testcontroller.8.in \
>>      utilities/ovs-dpctl.8.in \
>> @@ -129,6 +130,7 @@ FLAKE8_PYFILES += utilities/ovs-pcap.in \
>>      utilities/checkpatch.py utilities/ovs-dev.py \
>>      utilities/ovs-check-dead-ifs.in \
>>      utilities/ovs-tcpdump.in \
>> -    utilities/ovs-pipegen.py
>> +    utilities/ovs-pipegen.py \
>> +    utilities/usdt_scripts/upcall_monitor.py
>>
>>  include utilities/bugtool/automake.mk
>> diff --git a/utilities/usdt_scripts/upcall_monitor.py 
>> b/utilities/usdt_scripts/upcall_monitor.py
>> new file mode 100755
>> index 000000000..5c3076b66
>> --- /dev/null
>> +++ b/utilities/usdt_scripts/upcall_monitor.py
>> @@ -0,0 +1,500 @@
>> +#!/usr/bin/env python3
>> +#
>> +# Copyright (c) 2021 Red Hat, Inc.
>> +#
>> +# Licensed under the Apache License, Version 2.0 (the "License");
>> +# you may not use this file except in compliance with the License.
>> +# You may obtain a copy of the License at:
>> +#
>> +#     http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing, software
>> +# distributed under the License is distributed on an "AS IS" BASIS,
>> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>> +# See the License for the specific language governing permissions and
>> +# limitations under the License.
>> +#
>> +# Script information:
>> +# -------------------
>> +# upcall_monitor.py uses the dpif_recv:recv_upcall USDT to receive all 
>> upcall
>> +# packets sent by the kernel to ovs-vswitchd. By default, it will show all
>> +# upcall events, which looks something like this:
>> +#
>> +# TIME               CPU  COMM      PID      DPIF_NAME          TYPE 
>> PKT_LEN...
>> +# 5952147.003848809  2    handler4  1381158  system@ovs-system  0    98     
>> 132
>> +# 5952147.003879643  2    handler4  1381158  system@ovs-system  0    70     
>> 160
>> +# 5952147.003914924  2    handler4  1381158  system@ovs-system  0    98     
>> 152
>> +#
>> +# In addition, the packet and flow key data can be dumped. This can be done
>> +# using the --packet-decode and --flow-key decode options (see below).
>> +#
>> +# Note that by default only 64 bytes of the packet and flow key are 
>> retrieved.
>> +# If you would like to capture all or more of the packet and/or flow key 
>> data,
>> +# the ----packet-size and --flow-key-size options can be used.
>> +#
>> +# If required, the received packets can also be stored in a pcap file using 
>> the
>> +# --pcap option.
>> +#
>> +# The following are the available options:
>> +#
>> +#    usage: upcall_monitor.py [-h] [-D [DEBUG]] [-d {none,hex,decode}]
>> +#                             [-f [64-2048]] [-k {none,hex,nlraw}]
>> +#                             [-p VSWITCHD_PID] [-s [64-2048]] [-w 
>> PCAP_FILE]
>> +#
>> +#    optional arguments:
>> +#      -h, --help            show this help message and exit
>> +#      -D [DEBUG], --debug [DEBUG]
>> +#                            Enable eBPF debugging
>> +#      -d {none,hex,decode}, --packet-decode {none,hex,decode}
>> +#                            Display packet content in selected mode,
>> +#                            default none
>> +#      -f [64-2048], --flow-key-size [64-2048]
>> +#                            Set maximum flow key size to capture, default 
>> 64
>> +#      -k {none,hex,nlraw}, --flow-key-decode {none,hex,nlraw}
>> +#                            Display flow-key content in selected mode, 
>> default
>> +#                            none
>> +#      -p VSWITCHD_PID, --pid VSWITCHD_PID
>> +#                            ovs-vswitch's PID
>> +#      -s [64-2048], --packet-size [64-2048]
>> +#                            Set maximum packet size to capture, default 64
>> +#      -w PCAP_FILE, --pcap PCAP_FILE
>> +#                            Write upcall packets to specified pcap file.
>> +#
>> +# The following is an example of how to use the script on the running
>> +# ovs-vswitchd process with a packet and flow key dump enabled:
>> +#
>> +#  $ ./upcall_monitor.py --packet-decode decode --flow-key-decode nlraw \
>> +#      --packet-size 128 --flow-key-size 256
>> +#  TIME               CPU  COMM             PID        DPIF_NAME          
>> ...
>> +#  5953013.333214231  2    handler4         1381158    system@ovs-system  
>> ...
>> +#    Flow key size 132 bytes, size captured 132 bytes.
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_RECIRC_ID[20], data: 00 00 00 00
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_DP_HASH[19], data: 00 00 00 00
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_PRIORITY[2], data: 00 00 00 00
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_IN_PORT[3], data: 02 00 00 00
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_SKB_MARK[15], data: 00 00 00 00
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_CT_STATE[22], data: 00 00 00 00
>> +#      nla_len 6, nla_type OVS_KEY_ATTR_CT_ZONE[23], data: 00 00
>> +#      nla_len 8, nla_type OVS_KEY_ATTR_CT_MARK[24], data: 00 00 00 00
>> +#      nla_len 20, nla_type OVS_KEY_ATTR_CT_LABELS[25], data: 00 00 00 00 
>> ...
>> +#      nla_len 16, nla_type OVS_KEY_ATTR_ETHERNET[4], data: 04 f4 bc 28 57 
>> ...
>> +#      nla_len 6, nla_type OVS_KEY_ATTR_ETHERTYPE[6], data: 08 00
>> +#      nla_len 16, nla_type OVS_KEY_ATTR_IPV4[7], data: 01 01 01 64 01 01 
>> ...
>> +#      nla_len 6, nla_type OVS_KEY_ATTR_ICMP[11], data: 00 00
>> +#    1: Receive dp_port 2, packet size 98 bytes, size captured 98 bytes.
>> +#      ###[ Ethernet ]###
>> +#        dst       = 3c:fd:fe:9e:7f:68
>> +#        src       = 04:f4:bc:28:57:01
>> +#        type      = IPv4
>> +#      ###[ IP ]###
>> +#           version   = 4
>> +#           ihl       = 5
>> +#           tos       = 0x0
>> +#           len       = 84
>> +#           id        = 41404
>> +#           flags     = DF
>> +#           frag      = 0
>> +#           ttl       = 64
>> +#           proto     = icmp
>> +#           chksum    = 0x940c
>> +#           src       = 1.1.1.100
>> +#           dst       = 1.1.1.123
>> +#           \options   \
>> +#      ###[ ICMP ]###
>> +#              type      = echo-reply
>> +#              code      = 0
>> +#              chksum    = 0x2f55
>> +#              id        = 0x90e6
>> +#              seq       = 0x1
>> +#      ###[ Raw ]###
>> +#                 load      = 
>> 'GBTa\x00\x00\x00\x00\xd8L\r\x00\x00\x00\x00\...
>> +#
>> +
>> +from bcc import BPF, USDT, USDTException
>> +from os.path import exists
>> +from scapy.all import hexdump, wrpcap
>> +from scapy.layers.l2 import Ether
>> +
>> +import argparse
>> +import psutil
>> +import re
>> +import struct
>> +import sys
>> +import time
>> +
>> +#
>> +# Actual eBPF source code
>> +#
>> +ebpf_source = """
>> +#include <linux/sched.h>
>> +
>> +#define MAX_PACKET <MAX_PACKET_VAL>
>> +#define MAX_KEY    <MAX_KEY_VAL>
>> +
>> +struct event_t {
>> +    u32 cpu;
>> +    u32 pid;
>> +    u32 upcall_type;
>> +    u64 ts;
>> +    u32 pkt_size;
>> +    u64 key_size;
>> +    char comm[TASK_COMM_LEN];
>> +    char dpif_name[32];
>> +    unsigned char pkt[MAX_PACKET];
>> +    unsigned char key[MAX_KEY];
>> +};
>> +BPF_RINGBUF_OUTPUT(events, 1024);
>> +
>
> It seems a reasonable value for the ringbuf.
> Does it make sense to add an option and make this replaceable, maybe
> defaulting to 1024 if not provided? (same thing you did in the next
> patch).

Sound good! I will add the --buffer-page-count option and an event miss counter 
(with a warning).


>> +int do_trace(struct pt_regs *ctx) {
>> +    uint64_t addr;
>> +    uint64_t size;
>> +
>> +    struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t));
>> +    if (!event) {
>> +        return 1;
>> +    }
>> +
>> +    event->ts = bpf_ktime_get_ns();
>> +    event->cpu =  bpf_get_smp_processor_id();
>> +    event->pid = bpf_get_current_pid_tgid();
>> +    bpf_get_current_comm(&event->comm, sizeof(event->comm));
>> +
>> +    bpf_usdt_readarg(1, ctx, &addr);
>> +    bpf_probe_read(&event->dpif_name, sizeof(event->dpif_name), (void 
>> *)addr);
>> +
>
> Out of curiosity, is there any reason you didn't use
> bpf_probe_read_str()?

No idea why I did not, will replace it with bpf_probe_read_str().

>> +    bpf_usdt_readarg(2, ctx, &event->upcall_type);
>> +    bpf_usdt_readarg(4, ctx, &event->pkt_size);
>> +    bpf_usdt_readarg(6, ctx, &event->key_size);
>> +
>> +    if (event->pkt_size > MAX_PACKET)
>> +        size = MAX_PACKET;
>> +    else
>> +        size = event->pkt_size;
>> +    bpf_usdt_readarg(3, ctx, &addr);
>> +    bpf_probe_read(&event->pkt, size, (void *)addr);
>> +
>> +    if (event->key_size > MAX_KEY)
>> +        size = MAX_KEY;
>> +    else
>> +        size = event->key_size;
>> +    bpf_usdt_readarg(5, ctx, &addr);
>> +    bpf_probe_read(&event->key, size, (void *)addr);
>> +
>> +    events.ringbuf_submit(event, 0);
>> +    return 0;
>> +};
>> +"""
>> +
>> +
>> +#
>> +# print_event()
>> +#
>> +def print_event(ctx, data, size):
>> +    event = b['events'].event(data)
>> +    print("{:<18.9f} {:<4} {:<16} {:<10} {:<32} {:<4} {:<10} {:<10}".
>> +          format(event.ts / 1000000000,
>> +                 event.cpu,
>> +                 event.comm.decode("utf-8"),
>> +                 event.pid,
>> +                 event.dpif_name.decode("utf-8"),
>> +                 event.upcall_type,
>> +                 event.pkt_size,
>> +                 event.key_size))
>> +
>> +    #
>> +    # Dump flow key information
>> +    #
>> +    if event.key_size < options.flow_key_size:
>> +        key_len = event.key_size
>> +    else:
>> +        key_len = options.flow_key_size
>> +
>> +    if options.flow_key_decode != 'none':
>> +        print("  Flow key size {} bytes, size captured {} bytes.".
>> +              format(event.key_size, key_len))
>> +
>> +    if options.flow_key_decode == 'hex':
>> +        #
>> +        # Abuse scapy's hex dump to dum flow key
>
> nit: typo "dump"?
>
>> +        ##

Fixed in V2, as well as the ##.

>> +        print(re.sub('^', ' ' * 4, 
>> hexdump(Ether(bytes(event.key)[:key_len]),
>> +                                           dump=True),
>> +                     flags=re.MULTILINE))
>> +
>> +    if options.flow_key_decode == 'nlraw':
>> +        nla = decode_nlm(bytes(event.key)[:key_len])
>> +    else:
>> +        nla = decode_nlm(bytes(event.key)[:key_len], dump=False)
>> +
>> +    if "OVS_KEY_ATTR_IN_PORT" in nla:
>> +        port = struct.unpack("=I", nla["OVS_KEY_ATTR_IN_PORT"])[0]
>> +    else:
>> +        port = "Unknown"
>> +
>> +    #
>> +    # Decode packet only if there is data
>> +    #
>> +    if event.pkt_size <= 0:
>> +        return
>> +
>> +    pkt_id = get_pkt_id()
>> +
>> +    if event.pkt_size < options.packet_size:
>> +        pkt_len = event.pkt_size
>> +        pkt_data = bytes(event.pkt)[:event.pkt_size]
>> +    else:
>> +        pkt_len = options.packet_size
>> +        pkt_data = bytes(event.pkt)
>> +
>> +    if options.packet_decode != 'none' or options.pcap is not None:
>> +        print("  {}: Receive dp_port {}, packet size {} bytes, size "
>> +              "captured {} bytes.".format(pkt_id, port, event.pkt_size,
>> +                                          pkt_len))
>> +
>> +    if options.packet_decode == 'hex':
>> +        print(re.sub('^', ' ' * 4, hexdump(pkt_data, dump=True),
>> +                     flags=re.MULTILINE))
>> +
>> +    packet = Ether(pkt_data)
>> +    packet.wirelen = event.pkt_size
>> +
>> +    if options.packet_decode == 'decode':
>> +        print(re.sub('^', ' ' * 4, packet.show(dump=True), 
>> flags=re.MULTILINE))
>> +
>> +    if options.pcap is not None:
>> +        wrpcap(options.pcap, packet, append=True, 
>> snaplen=options.packet_size)
>> +
>> +
>> +#
>> +# decode_nlm()
>> +#
>> +def decode_nlm(msg, indent=4, dump=True):
>> +    bytes_left = len(msg)
>> +    result = {}
>> +
>> +    while bytes_left:
>> +        if bytes_left < 4:
>> +            if dump:
>> +                print("{}WARN: decode truncated; can't read header".format(
>> +                    ' ' * indent))
>> +            break
>> +
>> +        nla_len, nla_type = struct.unpack("=HH", msg[:4])
>> +
>> +        if nla_len < 4:
>> +            if dump:
>> +                print("{}WARN: decode truncated; nla_len < 4".format(
>> +                    ' ' * indent))
>> +            break
>> +
>> +        nla_data = msg[4:nla_len]
>> +        trunc = ""
>> +
>> +        if nla_len > bytes_left:
>> +            trunc = "..."
>> +            nla_data = nla_data[:(bytes_left - 4)]
>> +        else:
>> +            result[get_ovs_key_attr_str(nla_type)] = nla_data
>> +
>> +        if dump:
>> +            print("{}nla_len {}, nla_type {}[{}], data: {}{}".format(
>> +                ' ' * indent, nla_len, get_ovs_key_attr_str(nla_type),
>> +                nla_type,
>> +                "".join("{:02x} ".format(b) for b in nla_data), trunc))
>> +
>> +        if trunc != "":
>> +            if dump:
>> +                print("{}WARN: decode truncated; nla_len > msg_len[{}] ".
>> +                      format(' ' * indent, bytes_left))
>> +            break
>> +
>> +        # update next offset, but make sure it's aligned correctly
>> +        next_offset = (nla_len + 3) & ~(3)
>> +        msg = msg[next_offset:]
>> +        bytes_left -= next_offset
>> +
>> +    return result
>> +
>> +
>> +#
>> +# get_ovs_key_attr_str()
>> +#
>> +def get_ovs_key_attr_str(attr):
>> +    ovs_key_attr = ["OVS_KEY_ATTR_UNSPEC",
>> +                    "OVS_KEY_ATTR_ENCAP",
>> +                    "OVS_KEY_ATTR_PRIORITY",
>> +                    "OVS_KEY_ATTR_IN_PORT",
>> +                    "OVS_KEY_ATTR_ETHERNET",
>> +                    "OVS_KEY_ATTR_VLAN",
>> +                    "OVS_KEY_ATTR_ETHERTYPE",
>> +                    "OVS_KEY_ATTR_IPV4",
>> +                    "OVS_KEY_ATTR_IPV6",
>> +                    "OVS_KEY_ATTR_TCP",
>> +                    "OVS_KEY_ATTR_UDP",
>> +                    "OVS_KEY_ATTR_ICMP",
>> +                    "OVS_KEY_ATTR_ICMPV6",
>> +                    "OVS_KEY_ATTR_ARP",
>> +                    "OVS_KEY_ATTR_ND",
>> +                    "OVS_KEY_ATTR_SKB_MARK",
>> +                    "OVS_KEY_ATTR_TUNNEL",
>> +                    "OVS_KEY_ATTR_SCTP",
>> +                    "OVS_KEY_ATTR_TCP_FLAGS",
>> +                    "OVS_KEY_ATTR_DP_HASH",
>> +                    "OVS_KEY_ATTR_RECIRC_ID",
>> +                    "OVS_KEY_ATTR_MPLS",
>> +                    "OVS_KEY_ATTR_CT_STATE",
>> +                    "OVS_KEY_ATTR_CT_ZONE",
>> +                    "OVS_KEY_ATTR_CT_MARK",
>> +                    "OVS_KEY_ATTR_CT_LABELS",
>> +                    "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",
>> +                    "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",
>> +                    "OVS_KEY_ATTR_NSH"]
>> +
>> +    if attr < 0 or attr > len(ovs_key_attr):
>> +        return "<UNKNOWN>"
>> +
>> +    return ovs_key_attr[attr]
>> +
>> +
>> +#
>> +# get_pkt_id()
>> +#
>> +def get_pkt_id():
>> +    if not hasattr(get_pkt_id, "counter"):
>> +        get_pkt_id.counter = 0
>> +    get_pkt_id.counter += 1
>> +    return get_pkt_id.counter
>> +
>> +
>> +#
>> +# buffer_size_type()
>> +#
>> +def buffer_size_type(astr, min=64, max=2048):
>> +    value = int(astr)
>> +    if min <= value <= max:
>> +        return value
>> +    else:
>> +        raise argparse.ArgumentTypeError(
>> +            'value not in range {}-{}'.format(min, max))
>> +
>> +
>> +#
>> +# main()
>> +#
>> +def main():
>> +    #
>> +    # Don't like these globals, but ctx passing does not seem to work with 
>> the
>> +    # existing open_ring_buffer() API :(
>> +    #
>> +    global b
>> +    global options
>> +
>> +    #
>> +    # Argument parsing
>> +    #
>> +    parser = argparse.ArgumentParser()
>> +
>> +    parser.add_argument("-D", "--debug",
>> +                        help="Enable eBPF debugging",
>> +                        type=int, const=0x3f, default=0, nargs='?')
>> +    parser.add_argument('-d', '--packet-decode',
>> +                        help='Display packet content in selected mode, '
>> +                        'default none',
>> +                        choices=['none', 'hex', 'decode'], default='none')
>> +    parser.add_argument("-f", "--flow-key-size",
>> +                        help="Set maximum flow key size to capture, "
>> +                        "default 64", type=buffer_size_type, default=64,
>> +                        metavar="[64-2048]")
>> +    parser.add_argument('-k', '--flow-key-decode',
>> +                        help='Display flow-key content in selected mode, '
>> +                        'default none',
>> +                        choices=['none', 'hex', 'nlraw'], default='none')
>> +    parser.add_argument("-p", "--pid", metavar="VSWITCHD_PID",
>> +                        help="ovs-vswitch's PID",
>> +                        type=int, default=None)
>> +    parser.add_argument("-s", "--packet-size",
>> +                        help="Set maximum packet size to capture, "
>> +                        "default 64", type=buffer_size_type, default=64,
>> +                        metavar="[64-2048]")
>> +    parser.add_argument("-w", "--pcap", metavar="PCAP_FILE",
>> +                        help="Write upcall packets to specified pcap file.",
>> +                        type=str, default=None)
>> +
>> +    options = parser.parse_args()
>> +
>> +    #
>> +    # Find the PID of the ovs-vswitchd daemon if not specified.
>> +    #
>> +    if options.pid is None:
>> +        for proc in psutil.process_iter():
>> +            if 'ovs-vswitchd' in proc.name():
>> +                if options.pid is not None:
>> +                    print("ERROR: Multiple ovs-vswitchd daemons running, "
>> +                          "use the -p option!")
>> +                    sys.exit(-1)
>> +
>> +                options.pid = proc.pid
>> +
>> +    #
>> +    # Error checking on input parameters
>> +    #
>> +    if options.pid is None:
>> +        print("ERROR: Failed to find ovs-vswitchd's PID!")
>> +        sys.exit(-1)
>> +
>> +    if options.pcap is not None:
>> +        if exists(options.pcap):
>> +            print("ERROR: destination Capture file \"{}\" already exists!".
>> +                  format(options.pcap))
>
> nit: the capital letter should be fixed

Fixed in v2.

>> +            sys.exit(-1)
>> +
>> +    #
>> +    # Attach the usdt probe
>> +    #
>> +    u = USDT(pid=int(options.pid))
>> +    try:
>> +        u.enable_probe(probe="recv_upcall", fn_name="do_trace")
>> +    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")))
>> +        sys.exit(-1)
>> +
>> +    #
>> +    # Uncomment to see how arguments are decoded.
>> +    #   print(u.get_text())
>> +    #
>> +
>> +    #
>> +    # Attach probe to running process
>> +    #
>> +    source = ebpf_source.replace("<MAX_PACKET_VAL>", 
>> str(options.packet_size))
>> +    source = source.replace("<MAX_KEY_VAL>", str(options.flow_key_size))
>> +    b = BPF(text=source, usdt_contexts=[u], debug=options.debug)
>> +
>> +    #
>> +    # Print header
>> +    #
>> +    print("{:<18} {:<4} {:<16} {:<10} {:<32} {:<4} {:<10} {:<10}".format(
>> +        "TIME", "CPU", "COMM", "PID", "DPIF_NAME", "TYPE", "PKT_LEN",
>> +        "FLOW_KEY_LEN"))
>> +
>> +    #
>> +    # Dump out all events
>> +    #
>> +    b['events'].open_ring_buffer(print_event)
>> +    while 1:
>> +        try:
>> +            b.ring_buffer_poll()
>> +            time.sleep(0.5)
>> +        except KeyboardInterrupt:
>> +            break
>> +
>> +
>> +#
>> +# Start main() as the default entry point...
>> +#
>> +if __name__ == '__main__':
>> +    main()
>>
>> _______________________________________________
>> dev mailing list
>> [email protected]
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to