Tracing Openflow with ovnkube-trace
To trace Open vSwitch and OVN traffic flows in OpenShift Container Platform, you can use the ovnkube-trace utility, which runs ovn-trace, ovs-appctl ofproto/trace, and ovn-detrace in a single correlated output.
You can execute the ovnkube-trace binary from a dedicated container. For releases after OpenShift Container Platform 4.7, you can also copy the binary to a local host and execute it from that host.
Installing the ovnkube-trace on local host
To run ovnkube-trace from your local host against a cluster, you can copy the binary from an ovnkube-control-plane pod and use familiar Kubernetes namespace and pod arguments.
The ovnkube-trace tool traces packet simulations for arbitrary UDP or TCP traffic between points in an OVN-Kubernetes driven OpenShift Container Platform cluster.
Prerequisites
- You have installed the OpenShift CLI (
oc). - You are logged in to the cluster with
cluster-adminprivileges.
Procedure
-
Create a pod variable by using the following command:
$ POD=$(oc get pods -n openshift-ovn-kubernetes -l app=ovnkube-control-plane -o name | head -1 | awk -F '/' '{print $NF}') -
Run the following command on your local host to copy the binary from the
ovnkube-control-planepods:$ oc cp -n openshift-ovn-kubernetes $POD:/usr/bin/ovnkube-trace -c ovnkube-cluster-manager ovnkube-tracenoteIf you are using Red Hat Enterprise Linux (RHEL) 8 to run the
ovnkube-tracetool, you must copy the file/usr/lib/rhel8/ovnkube-traceto your local host. -
Make
ovnkube-traceexecutable by running the following command:$ chmod +x ovnkube-trace -
Display the options available with
ovnkube-traceby running the following command:$ ./ovnkube-trace -helpExample outputUsage of ./ovnkube-trace:-addr-family stringAddress family (ip4 or ip6) to be used for tracing (default "ip4")-dst stringdest: destination pod name-dst-ip stringdestination IP address (meant for tests to external targets)-dst-namespace stringk8s namespace of dest pod (default "default")-dst-port stringdst-port: destination port (default "80")-kubeconfig stringabsolute path to the kubeconfig file-loglevel stringloglevel: klog level (default "0")-ovn-config-namespace stringnamespace used by ovn-config itself-service stringservice: destination service name-skip-detraceskip ovn-detrace command-src stringsrc: source pod name-src-namespace stringk8s namespace of source pod (default "default")-tcpuse tcp transport protocol-udpuse udp transport protocolThe command-line arguments supported are familiar Kubernetes constructs, such as namespaces, pods, services so you do not need to find the MAC address, the IP address of the destination nodes, or the ICMP type.
The log levels are:
- 0 (minimal output)
- 2 (more verbose output showing results of trace commands)
- 5 (debug output)
Running ovnkube-trace
To simulate packet forwarding in an OVN logical network, you can run ovnkube-trace with source and destination pods, ports, and log levels.
The examples in the following procedure show DNS resolution and default-deny network policy troubleshooting.
Prerequisites
- You have installed the OpenShift CLI (
oc). - You are logged in to the cluster with a user with
cluster-adminprivileges. - You have installed the
ovnkube-tracebinary on your local host.
Procedure
-
Start a web service in the default namespace by entering the following command:
$ oc run web --namespace=default --image=quay.io/openshifttest/nginx --labels="app=web" --expose --port=80 -
List the pods running in the
openshift-dnsnamespace:$ oc get pods -n openshift-dnsThe output is similar to the following:
NAME READY STATUS RESTARTS AGEdns-default-8s42x 2/2 Running 0 5h8mdns-default-mdw6r 2/2 Running 0 4h58mdns-default-p8t5h 2/2 Running 0 4h58mdns-default-rl6nk 2/2 Running 0 5h8mdns-default-xbgqx 2/2 Running 0 5h8mdns-default-zv8f6 2/2 Running 0 4h58mnode-resolver-62jjb 1/1 Running 0 5h8mnode-resolver-8z4cj 1/1 Running 0 4h59mnode-resolver-bq244 1/1 Running 0 5h8mnode-resolver-hc58n 1/1 Running 0 4h59mnode-resolver-lm6z4 1/1 Running 0 5h8mnode-resolver-zfx5k 1/1 Running 0 5h -
Run the following
ovnkube-tracecommand to verify DNS resolution is working:$ ./ovnkube-trace \-src-namespace default \-src web \-dst-namespace openshift-dns \-dst dns-default-p8t5h \-udp -dst-port 53 \-loglevel 0where:
-src-namespace- Specifies namespace of the source pod.
-src- Specifies source pod name.
-dst-namespace- Specifies namespace of destination pod.
-dst- Specifies destination pod name.
-udp- Specifies use of the
udptransport protocol. Port 53 is the port the DNS service uses. -loglevel- Specifies the log level to 0 (0 is minimal and 5 is debug). If the
src&dstpod lands on the same node, the output is similar to the following:
ovn-trace source pod to destination pod indicates success from web to dns-default-p8t5hovn-trace destination pod to source pod indicates success from dns-default-p8t5h to webovs-appctl ofproto/trace source pod to destination pod indicates success from web to dns-default-p8t5hovs-appctl ofproto/trace destination pod to source pod indicates success from dns-default-p8t5h to webovn-detrace source pod to destination pod indicates success from web to dns-default-p8t5hovn-detrace destination pod to source pod indicates success from dns-default-p8t5h to webIf the
src&dstpod lands on a different node, the output is similar to the following:ovn-trace source pod to destination pod indicates success from web to dns-default-8s42xovn-trace (remote) source pod to destination pod indicates success from web to dns-default-8s42xovn-trace destination pod to source pod indicates success from dns-default-8s42x to webovn-trace (remote) destination pod to source pod indicates success from dns-default-8s42x to webovs-appctl ofproto/trace source pod to destination pod indicates success from web to dns-default-8s42xovs-appctl ofproto/trace destination pod to source pod indicates success from dns-default-8s42x to webovn-detrace source pod to destination pod indicates success from web to dns-default-8s42xovn-detrace destination pod to source pod indicates success from dns-default-8s42x to webThe output indicates success from the deployed pod to the DNS port and also indicates that it is successful going back in the other direction. So you know bi-directional traffic is supported on UDP port 53 if my web pod wants to do dns resolution from core DNS. If for example that did not work and you wanted to get the
ovn-trace, theovs-appctlofproto/traceandovn-detrace, and more debug type information increase the log level to 2 and run the command again as follows:$ ./ovnkube-trace \-src-namespace default \-src web \-dst-namespace openshift-dns \-dst dns-default-467qw \-udp -dst-port 53 \-loglevel 2The output from this increased log level is too much to list here. In a failure situation the output of this command shows which flow is dropping that traffic. For example an egress or ingress network policy may be configured on the cluster that does not allow that traffic.
Testing a default deny policy with ovnkube-trace
To verify that an ingress default deny network policy blocks traffic in OpenShift Container Platform, you can run ovnkube-trace with a higher log level and read the ACL debug output. You can add an allow policy for labeled namespaces and confirm that traffic succeeds.
Prerequisites
- You have installed the OpenShift CLI (
oc). - You are logged in to the cluster with a user with
cluster-adminprivileges. - You have installed the
ovnkube-tracebinary on your local host.
Procedure
-
Create the following YAML that defines a
deny-by-defaultpolicy to deny ingress from all pods in all namespaces. Save the YAML in thedeny-by-default.yamlfile:kind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata:name: deny-by-defaultnamespace: defaultspec:podSelector: {}ingress: [] -
Apply the policy by entering the following command:
$ oc apply -f deny-by-default.yamlExample outputnetworkpolicy.networking.k8s.io/deny-by-default created -
Start a web service in the
defaultnamespace by entering the following command:$ oc run web --namespace=default --image=quay.io/openshifttest/nginx --labels="app=web" --expose --port=80 -
Run the following command to create the
prodnamespace:$ oc create namespace prod -
Run the following command to label the
prodnamespace:$ oc label namespace/prod purpose=production -
To deploy an
alpineimage in theprodnamespace and start a shell, run the following command:$ oc run test-6459 --namespace=prod --rm -i -t --image=alpine -- sh -
Open another terminal session.
-
In this new terminal session run
ovn-traceto verify the failure in communication between the source podtest-6459running in namespaceprodand destination pod running in thedefaultnamespace:$ ./ovnkube-trace \-src-namespace prod \-src test-6459 \-dst-namespace default \-dst web \-tcp -dst-port 80 \-loglevel 0Example outputovn-trace source pod to destination pod indicates failure from test-6459 to web -
Increase the log level to 2 to expose the reason for the failure by running the following command:
$ ./ovnkube-trace \-src-namespace prod \-src test-6459 \-dst-namespace default \-dst web \-tcp -dst-port 80 \-loglevel 2Example output...------------------------------------------------3. ls_out_acl_hint (northd.c:7454): !ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0, priority 4, uuid 12efc456reg0[8] = 1;reg0[10] = 1;next;5. ls_out_acl_action (northd.c:7835): reg8[30..31] == 0, priority 500, uuid 69372c5dreg8[30..31] = 1;next(4);5. ls_out_acl_action (northd.c:7835): reg8[30..31] == 1, priority 500, uuid 2fa0af89reg8[30..31] = 2;next(4);4. ls_out_acl_eval (northd.c:7691): reg8[30..31] == 2 && reg0[10] == 1 && (outport == @a16982411286042166782_ingressDefaultDeny), priority 2000, uuid 447d0dabreg8[17] = 1;ct_commit { ct_mark.blocked = 1; };next;...where:
ct_commit { ct_mark.blocked = 1; };- Specifies that ingress traffic is blocked due to the default deny policy being in place.
-
Create a policy that allows traffic from all pods in a particular namespaces with a label
purpose=production. Save the YAML in theweb-allow-prod.yamlfile:kind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata:name: web-allow-prodnamespace: defaultspec:podSelector:matchLabels:app: webpolicyTypes:- Ingressingress:- from:- namespaceSelector:matchLabels:purpose: production -
Apply the policy by entering the following command:
$ oc apply -f web-allow-prod.yaml -
Run
ovnkube-traceto verify that traffic is now allowed by entering the following command:$ ./ovnkube-trace \-src-namespace prod \-src test-6459 \-dst-namespace default \-dst web \-tcp -dst-port 80 \-loglevel 0Example outputovn-trace source pod to destination pod indicates success from test-6459 to webovn-trace destination pod to source pod indicates success from web to test-6459ovs-appctl ofproto/trace source pod to destination pod indicates success from test-6459 to webovs-appctl ofproto/trace destination pod to source pod indicates success from web to test-6459ovn-detrace source pod to destination pod indicates success from test-6459 to webovn-detrace destination pod to source pod indicates success from web to test-6459 -
Run the following command in the shell that was opened in step six to connect nginx to the web-server:
$ wget -qO- --timeout=2 http://web.defaultExample output<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.</p><p>For online documentation and support please refer to<a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p></body></html>
Additional resources