Skip to content

Commit

Permalink
calico-bpf conntrack dump has less verbose output
Browse files Browse the repository at this point in the history
Not to bother user with too much information, the output shows where a
connection goes from and too. If there is a service invoplved in the
middle, it shows that too. You can get the orifginal output using the
--raw  option.

UDP 172.18.0.1:35199 -> 239.255.255.250:1900  Age: 56.28257308s Active ago 53.279924595s
TCP 172.18.0.9:57736 -> 172.18.0.7:30333 -> 10.65.0.2:8055  Age: 2.535576963s Active ago 493.47131ms SYN-SENT
UDP 172.18.0.1:5353 -> 224.0.0.251:5353  Age: 57.284951727s Active ago 50.27747628s
UDP 172.18.0.1:37213 -> 239.255.255.250:1900  Age: 59.283295671s Active ago 57.282576585s
TCP 172.18.0.1:52424 -> 172.18.0.7:9099  Age: 58.300322297s Active ago 12.945516929s ESTABLISHED

TCP 172.18.0.9:57162 -> 172.18.0.6:30333 -> 10.65.0.2:8055 external client, service forwarded to/from 172.18.0.6  Age: 6.107113931s Active ago 988.204797ms SYN-SENT
  • Loading branch information
tomastigera committed Apr 23, 2024
1 parent f4b77b2 commit c172d39
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 34 deletions.
138 changes: 110 additions & 28 deletions felix/cmd/calico-bpf/commands/conntrack.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"encoding/base64"
"fmt"
"net"
"os"
"strconv"
"strings"
"time"

Expand All @@ -26,6 +28,7 @@ import (
"github.com/projectcalico/calico/felix/bpf"
"github.com/projectcalico/calico/felix/bpf/conntrack"
v2 "github.com/projectcalico/calico/felix/bpf/conntrack/v2"
v3 "github.com/projectcalico/calico/felix/bpf/conntrack/v3"
"github.com/projectcalico/calico/felix/bpf/maps"

"github.com/docopt/docopt-go"
Expand All @@ -49,41 +52,48 @@ func init() {
}

// conntrackCmd represents the conntrack command
var conntrackCmd = &cobra.Command{
Use: "conntrack",
Short: "Manipulates connection tracking",
}
var (
conntrackCmd = &cobra.Command{
Use: "conntrack",
Short: "Manipulates connection tracking",
}

voidIP4 = net.IPv4(0, 0, 0, 0)
voidIP6 = net.ParseIP("::")
)

type conntrackDumpCmd struct {
*cobra.Command
Version string `docopt:"--ver"`
version string
version int
raw bool
ipv6 bool
}

func newConntrackDumpCmd() *cobra.Command {
cmd := &conntrackDumpCmd{
Command: &cobra.Command{
Use: "dump [--ver=<version>]",
Use: "dump [--ver=<version>] [--raw]",
Short: "Dumps connection tracking table",
},
}
cmd.Command.Flags().StringVarP((&cmd.version), "ver", "v", "", "version to dump from")

var version string

cmd.Command.Flags().StringVarP((&version), "ver", "v", "", "version to dump from")
cmd.Command.Flags().BoolVar((&cmd.raw), "raw", false, "dump the raw conntrack table as is. For version < 3 it is always raw")
cmd.Command.Args = cmd.Args
cmd.Command.Run = cmd.Run

return cmd.Command
}

func (cmd *conntrackDumpCmd) Args(c *cobra.Command, args []string) error {
a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
if err != nil {
return errors.New(err.Error())
}
err = a.Bind(cmd)
if err != nil {
return errors.New(err.Error())
if version != "" {
v, err := strconv.Atoi(version)
if err != nil {
cmd.PrintErr("--ver needs to be a number")
os.Exit(-1)
}
cmd.version = v
}
return nil

return cmd.Command
}

func dumpCtMapV2(ctMap maps.Map) error {
Expand All @@ -110,11 +120,17 @@ func dumpCtMapV2(ctMap maps.Map) error {

func (cmd *conntrackDumpCmd) Run(c *cobra.Command, _ []string) {
var ctMap maps.Map

cmd.ipv6 = ipv6 != nil && *ipv6
if cmd.version < 3 && cmd.version != 0 {
cmd.raw = true
}

switch cmd.version {
case "2":
case 2:
ctMap = conntrack.MapV2()
default:
if ipv6 != nil && *ipv6 {
if cmd.ipv6 {
ctMap = conntrack.MapV6()
} else {
ctMap = conntrack.Map()
Expand All @@ -123,7 +139,7 @@ func (cmd *conntrackDumpCmd) Run(c *cobra.Command, _ []string) {
if err := ctMap.Open(); err != nil {
log.WithError(err).Fatal("Failed to access ConntrackMap")
}
if cmd.version == "2" {
if cmd.version == 2 {
err := dumpCtMapV2(ctMap)
if err != nil {
log.WithError(err).Fatal("Failed to iterate over conntrack entries")
Expand All @@ -133,7 +149,7 @@ func (cmd *conntrackDumpCmd) Run(c *cobra.Command, _ []string) {

keyFromBytes := conntrack.KeyFromBytes
valFromBytes := conntrack.ValueFromBytes
if ipv6 != nil && *ipv6 {
if cmd.ipv6 {
keyFromBytes = conntrack.KeyV6FromBytes
valFromBytes = conntrack.ValueV6FromBytes
}
Expand All @@ -142,16 +158,82 @@ func (cmd *conntrackDumpCmd) Run(c *cobra.Command, _ []string) {
ctKey := keyFromBytes(k)
ctVal := valFromBytes(v)

fmt.Printf("%v -> %v", ctKey, ctVal)
dumpExtra(ctKey, ctVal)
fmt.Printf("\n")
if cmd.raw {
fmt.Printf("%v -> %v", ctKey, ctVal)
dumpExtra(ctKey, ctVal)
fmt.Printf("\n")
} else {
cmd.prettyDump(ctKey, ctVal)
}
return maps.IterNone
})
if err != nil {
log.WithError(err).Fatal("Failed to iterate over conntrack entries")
}
}

func protoStr(proto uint8) string {
switch proto {
case 6:
return "TCP"
case 17:
return "UDP"
case 1:
return "ICMP"
case 58:
return "ICMP6"
}

return "UNKNOWN"
}

func (cmd *conntrackDumpCmd) prettyDump(k conntrack.KeyInterface, v conntrack.ValueInterface) {
d := v.Data()

switch v.Type() {
case conntrack.TypeNormal:
if v.Flags()&v3.FlagSrcDstBA != 0 {
cmd.Printf("%s %s:%d -> %s:%d ", protoStr(k.Proto()), k.AddrB(), k.PortB(), k.AddrA(), k.PortA())
} else {
cmd.Printf("%s %s:%d -> %s:%d ", protoStr(k.Proto()), k.AddrA(), k.PortA(), k.AddrB(), k.PortB())
}
case conntrack.TypeNATForward:
return
case conntrack.TypeNATReverse:
if v.Flags()&v3.FlagSrcDstBA != 0 {
cmd.Printf("%s %s:%d -> %s:%d -> %s:%d ",
protoStr(k.Proto()), k.AddrB(), k.PortB(), d.OrigDst, d.OrigPort, k.AddrA(), k.PortA())
} else {
cmd.Printf("%s %s:%d -> %s:%d -> %s:%d ",
protoStr(k.Proto()), k.AddrA(), k.PortA(), d.OrigDst, d.OrigPort, k.AddrB(), k.PortB())
}

if (cmd.ipv6 && !d.TunIP.Equal(voidIP6)) || (!cmd.ipv6 && !d.TunIP.Equal(voidIP4)) {
cmd.Printf("external client, service forwarded to/from %s ", d.TunIP)
}
}

if v.Flags()&v3.FlagHostPSNAT != 0 {
cmd.Printf("source port changed from %d ", d.OrigSPort)
}

now := bpf.KTimeNanos()
cmd.Printf(" Age: %s Active ago %s",
time.Duration(now-v.Created()), time.Duration(now-v.LastSeen()))

if k.Proto() == 6 {
if (v.IsForwardDSR() && d.FINsSeenDSR()) || d.FINsSeen() || d.RSTSeen() {
cmd.Printf(" CLOSED")
} else if d.Established() {
cmd.Printf(" ESTABLISHED")
} else {
cmd.Printf(" SYN-SENT")
}
}

cmd.Printf("\n")
}

func dumpExtrav2(k v2.Key, v v2.Value) {
now := bpf.KTimeNanos()

Expand All @@ -168,7 +250,7 @@ func dumpExtrav2(k v2.Key, v v2.Value) {

data := v.Data()

if (v.IsForwardDSR() && data.FINsSeenDSR()) || data.FINsSeen() {
if (v.IsForwardDSR() && data.FINsSeenDSR()) || data.FINsSeen() || data.RSTSeen() {
fmt.Printf(" CLOSED")
return
}
Expand Down
12 changes: 6 additions & 6 deletions felix/fv/bpf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func describeBPFTests(opts ...bpfTestOpt) bool {
felix.Exec("calico-bpf", "-6", "routes", "dump")
felix.Exec("calico-bpf", "-6", "nat", "dump")
felix.Exec("calico-bpf", "-6", "nat", "aff")
felix.Exec("calico-bpf", "-6", "conntrack", "dump")
felix.Exec("calico-bpf", "-6", "conntrack", "dump", "--raw")
felix.Exec("calico-bpf", "-6", "arp", "dump")
} else {
felix.Exec("iptables-save", "-c")
Expand All @@ -462,7 +462,7 @@ func describeBPFTests(opts ...bpfTestOpt) bool {
felix.Exec("calico-bpf", "routes", "dump")
felix.Exec("calico-bpf", "nat", "dump")
felix.Exec("calico-bpf", "nat", "aff")
felix.Exec("calico-bpf", "conntrack", "dump")
felix.Exec("calico-bpf", "conntrack", "dump", "--raw")
felix.Exec("calico-bpf", "arp", "dump")
}
felix.Exec("calico-bpf", "counters", "dump")
Expand Down Expand Up @@ -2389,9 +2389,9 @@ func describeBPFTests(opts ...bpfTestOpt) bool {
)

if testOpts.ipv6 {
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "-6", "dump")
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "-6", "dump", "--raw")
} else {
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "dump")
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "dump", "--raw")
}
Expect(err).NotTo(HaveOccurred())
re := regexp.MustCompile(`LastSeen:\s*(\d+)`)
Expand All @@ -2412,9 +2412,9 @@ func describeBPFTests(opts ...bpfTestOpt) bool {
// entries.
numWl0ConntrackEntries := func() int {
if testOpts.ipv6 {
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "-6", "dump")
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "-6", "dump", "--raw")
} else {
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "dump")
ctDump, err = tc.Felixes[0].ExecOutput("calico-bpf", "conntrack", "dump", "--raw")
}
Expect(err).NotTo(HaveOccurred())
return strings.Count(ctDump, w[0][0].IP)
Expand Down

0 comments on commit c172d39

Please sign in to comment.