Skip to content

Commit

Permalink
Merge pull request #4491 from qinqon/udn-vrf-table-id
Browse files Browse the repository at this point in the history
util: Move route table calculator from egressip
  • Loading branch information
tssurya authored Jul 3, 2024
2 parents b2a5770 + 27a9203 commit e7123c9
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 72 deletions.
4 changes: 3 additions & 1 deletion docs/troubleshooting/ovnkube-trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A tool to trace packet simulations for arbitrary UDP or TCP traffic between poin
Given the command-line arguments, ovnkube-trace would inspect the cluster to determine the addresses (MAC and IP) of the source and destination and perform `ovn-trace`, `ovs-appctl ofproto/trace`, and `ovn-detrace` from/to both directions.

```
Usage of _output/go/bin/ovnkube-trace:
Usage of /tmp/go-build1564673416/b001/exe/ovnkube-trace:
-addr-family string
Address family (ip4 or ip6) to be used for tracing (default "ip4")
-dst string
Expand All @@ -19,6 +19,8 @@ Usage of _output/go/bin/ovnkube-trace:
k8s namespace of dest pod (default "default")
-dst-port string
dst-port: destination port (default "80")
-dump-udn-vrf-table-ids
Dump the VRF table ID per node for all the user defined networks
-kubeconfig string
absolute path to the kubeconfig file
-loglevel string
Expand Down
27 changes: 27 additions & 0 deletions docs/troubleshooting/udn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# User Defined Networks

To debug UDN the ovnkube-trace can dump multiple elements of the topology to
make easier to match to what network they belong.

## Local gateway VRF table ID numbers for networks

Following command will dump the VRF table IDs from a local gateway system for
UDNs.

```bash
ovnkube-trace -dump-udn-vrf-table-ids
```

The output will be tableIDs indexed by node and network name
```json
{
"ovn-control-plane": {
"net-blue": 1,
"net-red": 2
},
"ovn-worker": {
"net-blue": 3,
"net-red": 4
}
}
```
85 changes: 48 additions & 37 deletions go-controller/cmd/ovnkube-trace/ovnkube-trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -1179,49 +1179,13 @@ func main() {
udp := flag.Bool("udp", false, "use udp transport protocol")
addressFamily := flag.String("addr-family", ip4, "Address family (ip4 or ip6) to be used for tracing")
skipOvnDetrace := flag.Bool("skip-detrace", false, "skip ovn-detrace command")
dumpVRFTableIDs := flag.Bool("dump-udn-vrf-table-ids", false, "Dump the VRF table ID per node for all the user defined networks")
loglevel := flag.String("loglevel", "0", "loglevel: klog level")
flag.Parse()

// Set the application's log level.
setLogLevel(*loglevel)

// Verify CLI flags.
if *srcPodName == "" {
klog.Exitf("Usage: source pod must be specified")
}
if !*tcp && !*udp {
klog.Exitf("Usage: either tcp or udp must be specified")
}
if *udp && *tcp {
klog.Exitf("Usage: Both tcp and udp cannot be specified at the same time")
}
if *tcp {
protocol = "tcp"
}
if *udp {
if *dstSvcName != "" {
klog.Exitf("Usage: udp option is not compatible with destination service trace")
}
protocol = "udp"
}
targetOptions := 0
if *dstPodName != "" {
targetOptions++
}
if *dstSvcName != "" {
targetOptions++
}
if *dstIP != "" {
targetOptions++
parsedDstIP = net.ParseIP(*dstIP)
if parsedDstIP == nil {
klog.Exitf("Usage: cannot parse IP address provided in -dst-ip")
}
}
if targetOptions != 1 {
klog.Exitf("Usage: exactly one of -dst, -service or -dst-ip must be set")
}

// Get the ClientConfig.
// This might work better? https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client/config
// When supplied the kubeconfig supplied via cli takes precedence
Expand Down Expand Up @@ -1258,8 +1222,55 @@ func main() {
if err != nil {
klog.Exitf(" Unexpected error: %v", err)
}

klog.V(5).Infof("OVN Kubernetes namespace is %s", ovnNamespace)

if *dumpVRFTableIDs {
nodesVRFTableIDs, err := findUserDefinedNetworkVRFTableIDs(coreclient, restconfig, ovnNamespace)
if err != nil {
klog.Exitf("Failed dumping VRF table IDs: %s", err)
}
fmt.Println(string(nodesVRFTableIDs))
return
}

// Verify CLI flags.
if *srcPodName == "" {
klog.Exitf("Usage: source pod must be specified")
}
if !*tcp && !*udp {
klog.Exitf("Usage: either tcp or udp must be specified")
}
if *udp && *tcp {
klog.Exitf("Usage: Both tcp and udp cannot be specified at the same time")
}
if *tcp {
protocol = "tcp"
}
if *udp {
if *dstSvcName != "" {
klog.Exitf("Usage: udp option is not compatible with destination service trace")
}
protocol = "udp"
}
targetOptions := 0
if *dstPodName != "" {
targetOptions++
}
if *dstSvcName != "" {
targetOptions++
}
if *dstIP != "" {
targetOptions++
parsedDstIP = net.ParseIP(*dstIP)
if parsedDstIP == nil {
klog.Exitf("Usage: cannot parse IP address provided in -dst-ip")
}
}
if targetOptions != 1 {
klog.Exitf("Usage: exactly one of -dst, -service or -dst-ip must be set")
}

// Show some information about the nodes in this cluster - only if log level 5 or higher.
if lvl, err := strconv.Atoi(*loglevel); err == nil && lvl >= 5 {
displayNodeInfo(coreclient)
Expand Down
82 changes: 82 additions & 0 deletions go-controller/cmd/ovnkube-trace/udn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"context"
"encoding/json"
"fmt"

types "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
util "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
)

func findUserDefinedNetworkVRFTableIDs(coreclient *corev1client.CoreV1Client, restconfig *rest.Config, ovnNamespace string) (string, error) {
nodeList, err := coreclient.Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return "", err
}
nodesTableIDs := map[string]map[string]uint{}
for _, node := range nodeList.Items {
networks, err := findNetworks(&node)
if err != nil {
return "", err
}
networksTableIDs := map[string]uint{}
for _, networkName := range networks {
tableID, err := findUserDefinedNetworkVRFTableID(coreclient, restconfig, &node, ovnNamespace, networkName)
if err != nil {
return "", err
}
networksTableIDs[networkName] = tableID
}
nodesTableIDs[node.Name] = networksTableIDs
}
nodesTableIDsJSON, err := json.Marshal(&nodesTableIDs)
if err != nil {
return "", err
}
return string(nodesTableIDsJSON), nil
}

func findUserDefinedNetworkVRFTableID(coreclient *corev1client.CoreV1Client, restconfig *rest.Config, node *corev1.Node, ovnNamespace, networkName string) (uint, error) {
ovnKubePodName, err := getOvnKubePodOnNode(coreclient, ovnNamespace, node.Name)
if err != nil {
return 0, err
}

mgmtPortLinkName := util.GetSecondaryNetworkPrefix(networkName) + types.K8sMgmtIntfName
ipLinkCmd := "ip -j link show dev " + mgmtPortLinkName
stdout, stderr, err := execInPod(coreclient, restconfig, ovnNamespace, ovnKubePodName, ovnKubeNodePodContainers[1], ipLinkCmd, "")
if err != nil {
return 0, fmt.Errorf("%s: %s: %w", stdout, stderr, err)
}
links := []struct {
Index uint `json:"ifindex"`
}{}
if err := json.Unmarshal([]byte(stdout), &links); err != nil {
return 0, err
}
if len(links) < 1 {
return 0, fmt.Errorf("link '%s' not found", mgmtPortLinkName)
}
return uint(util.CalculateRouteTableID(int(links[0].Index))), nil
}

func findNetworks(node *corev1.Node) ([]string, error) {
annotation, ok := node.Annotations["k8s.ovn.org/network-ids"]
if !ok {
return []string{}, nil
}
networkIDs := make(map[string]json.RawMessage)
if err := json.Unmarshal([]byte(annotation), &networkIDs); err != nil {
return nil, err
}
networks := []string{}
for networkName := range networkIDs {
networks = append(networks, networkName)
}
return networks, nil
}
10 changes: 3 additions & 7 deletions go-controller/pkg/node/controllers/egressip/egressip.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ func generateRoutesForLink(link netlink.Link, isV6 bool) ([]netlink.Route, error
return nil, fmt.Errorf("failed to get routes for link %s: %v", link.Attrs().Name, err)
}
linkRoutes = ensureAtLeastOneDefaultRoute(linkRoutes, link.Attrs().Index, isV6)
overwriteRoutesTableID(linkRoutes, getRouteTableID(link.Attrs().Index))
overwriteRoutesTableID(linkRoutes, util.CalculateRouteTableID(link.Attrs().Index))
return linkRoutes, nil
}

Expand Down Expand Up @@ -877,7 +877,7 @@ func (c *Controller) repairNode() error {
assignedAddrStrToAddrs[addressStr] = address
}
}
filter, mask := filterRouteByLinkTable(linkIdx, getRouteTableID(linkIdx))
filter, mask := filterRouteByLinkTable(linkIdx, util.CalculateRouteTableID(linkIdx))
existingRoutes, err := util.GetNetLinkOps().RouteListFiltered(netlink.FAMILY_ALL, filter, mask)
if err != nil {
return fmt.Errorf("unable to get route list using filter (%s): %v", filter.String(), err)
Expand Down Expand Up @@ -1320,10 +1320,6 @@ func overwriteRoutesTableID(routes []netlink.Route, tableID int) {
}
}

func getRouteTableID(ifIndex int) int {
return ifIndex + routingTableIDStart
}

func findLinkOnSameNetworkAsIP(ip net.IP, v4, v6 bool) (bool, netlink.Link, error) {
found, link, err := findLinkOnSameNetworkAsIPUsingLPM(ip, v4, v6)
if err != nil {
Expand Down Expand Up @@ -1437,7 +1433,7 @@ func getNetlinkAddress(addr *net.IPNet, ifindex int) *netlink.Addr {
// from the links 'ifindex'
func generateIPRule(srcIP net.IP, isIPv6 bool, ifIndex int) netlink.Rule {
r := *netlink.NewRule()
r.Table = getRouteTableID(ifIndex)
r.Table = util.CalculateRouteTableID(ifIndex)
r.Priority = rulePriority
var ipFullMask string
if isIPv6 {
Expand Down
Loading

0 comments on commit e7123c9

Please sign in to comment.