pkg/skoop/model/path.go (172 lines of code) (raw):
package model
import (
"fmt"
"strings"
"github.com/alibaba/kubeskoop/pkg/skoop/utils"
"github.com/samber/lo"
)
type LinkType string
const (
LinkExternal LinkType = "external"
LinkInfra LinkType = "infra"
LinkVeth LinkType = "veth"
LinkIPVlan LinkType = "ipvlan"
LinkLocal LinkType = "local"
)
type LinkAttribute interface {
GetAttrs() map[string]string
}
type SimpleLinkAttribute struct {
Interface string
IP string
}
func (a SimpleLinkAttribute) GetAttrs() map[string]string {
return map[string]string{
"if": a.Interface,
}
}
type VEthLinkAttribute struct {
SimpleLinkAttribute
PeerIndex int
}
func (v VEthLinkAttribute) GetAttrs() map[string]string {
attrs := v.SimpleLinkAttribute.GetAttrs()
attrs["peer_id"] = fmt.Sprintf("%d", v.PeerIndex)
return attrs
}
type NullAttribute struct {
}
func (a NullAttribute) GetAttrs() map[string]string {
return map[string]string{}
}
type Link struct {
Type LinkType
Source *NetNode
Destination *NetNode
Packet *Packet
SourceAttribute LinkAttribute
DestinationAttribute LinkAttribute
Level int // for print
}
func (l Link) GetID() string {
return fmt.Sprintf("%s,%s,%s,%s", l.Type, l.Source.GetID(), l.Destination.GetID(),
fmt.Sprintf("%s:%d", l.Packet.Dst, l.Packet.Dport))
}
type PacketPath struct {
origin *NetNode
}
func NewPacketPath(origin *NetNode) *PacketPath {
return &PacketPath{
origin: origin,
}
}
func (p *PacketPath) GetLinkLabel(link *Link, action *Action) string {
var buf []string
buf = append(buf, fmt.Sprintf("type=%s", link.Type))
buf = append(buf, fmt.Sprintf("level=%d", link.Level))
if action != nil {
buf = append(buf, fmt.Sprintf("trans=%s", action.Type))
}
srcAttrs := map[string]string{}
if link.SourceAttribute != nil {
srcAttrs = link.SourceAttribute.GetAttrs()
}
dstAttrs := map[string]string{}
if link.DestinationAttribute != nil {
dstAttrs = link.DestinationAttribute.GetAttrs()
}
if oif, ok := srcAttrs["if"]; ok {
buf = append(buf, fmt.Sprintf("oif=%s", oif))
}
if iif, ok := dstAttrs["if"]; ok {
buf = append(buf, fmt.Sprintf("iif=%s", iif))
}
buf = append(buf,
fmt.Sprintf("src=%s", link.Packet.Src),
fmt.Sprintf("dst=%s", link.Packet.Dst),
fmt.Sprintf("dport=%d", link.Packet.Dport))
return strings.Join(buf, ",")
}
func (p *PacketPath) GetOriginNode() *NetNode {
return p.origin
}
func (p *PacketPath) Links() []*Link {
var (
links []*Link
)
pendingLinks := utils.NewQueue[*Link]()
addLink := func(level int, action *Action) {
if action == nil {
return
}
for _, l := range action.Outputs {
l.Level = level
pendingLinks.Enqueue(l)
}
}
addLink(0, p.origin.ActionOf(nil))
for !pendingLinks.Empty() {
link := pendingLinks.Pop()
links = append(links, link)
addLink(link.Level+1, link.Destination.ActionOf(link))
}
return links
}
func (p *PacketPath) Paths() string {
buf := strings.Builder{}
links := p.Links()
if len(links) == 0 {
for _, n := range p.Nodes() {
buf.WriteString(fmt.Sprintf("\"%v\"", n.GetID()))
}
return buf.String()
}
for _, l := range links {
action := l.Destination.ActionOf(l)
attr := map[string]string{}
if action != nil {
label := p.GetLinkLabel(l, action)
attr["label"] = label
if action.Type == ActionTypeServe {
attr["arrowhead"] = "dot"
}
}
attrString := strings.Join(
lo.MapToSlice(attr, func(k, v string) string { return fmt.Sprintf("%s=%q", k, v) }), ",")
buf.WriteString(fmt.Sprintf("\"%v\" -> \"%v\" [%s]\n", l.Source.GetID(), l.Destination.GetID(), attrString))
}
return buf.String()
}
func (p *PacketPath) Nodes() []*NetNode {
var (
nodes []*NetNode
)
visited := map[string]struct{}{}
pendingLinks := utils.NewQueue[*Link]()
addLink := func(level int, action *Action) {
if action == nil {
return
}
for _, l := range action.Outputs {
l.Level = level
pendingLinks.Enqueue(l)
}
}
addLink(0, p.origin.ActionOf(nil))
for !pendingLinks.Empty() {
link := pendingLinks.Pop()
if _, ok := visited[link.Source.GetID()]; !ok {
visited[link.Source.GetID()] = struct{}{}
nodes = append(nodes, link.Source)
}
if _, ok := visited[link.Destination.GetID()]; !ok {
visited[link.Destination.GetID()] = struct{}{}
nodes = append(nodes, link.Destination)
}
addLink(link.Level+1, link.Destination.ActionOf(link))
}
if len(nodes) == 0 {
nodes = append(nodes, p.origin)
}
return nodes
}