in pkg/skoop/network/aliyun/assertion.go [190:309]
func (a *vpcAssertion) AssertRoute(srcECS, dstECS string, pkt *model.Packet, privateIP string) ([]model.Suspicion, error) {
var suspicions []model.Suspicion
var routeEntries []*vpc.DescribeRouteEntryListResponseBodyRouteEntrysRouteEntry
var srcECSInfo, dstECSInfo *aliyun.ECSInfo
var err error
if srcECS == "" && dstECS == "" {
return nil, nil
}
if srcECS != "" {
srcECSInfo, err = a.cloudManager.GetECSInfo(srcECS)
if err != nil {
return nil, err
}
routeEntries = srcECSInfo.Network.RouteTableEntries
if !slices.Contains(srcECSInfo.Network.IP, pkt.Src.String()) {
routeEntries = srcECSInfo.Network.VpcDefaultRouteTableEntries
}
lo.ForEach(srcECSInfo.Network.NetworkInterfaces, func(ni *aliyun.ENIInfo, _ int) {
lo.ForEach(ni.NetworkInterfaceSet.PrivateIpSets.PrivateIpSet, func(ip *ecs.DescribeNetworkInterfacesResponseBodyNetworkInterfaceSetsNetworkInterfaceSetPrivateIpSetsPrivateIpSet, _ int) {
if ip.PrivateIpAddress != nil && *ip.PrivateIpAddress == pkt.Src.String() {
routeEntries = ni.RouteTableEntries
}
})
})
}
if dstECS != "" {
dstECSInfo, err = a.cloudManager.GetECSInfo(dstECS)
if err != nil {
return nil, err
}
if srcECS == "" {
routeEntries = dstECSInfo.Network.VpcDefaultRouteTableEntries
}
}
dstRouteEntry, err := routeMatchPacket(pkt.Dst.String(), routeEntries)
if err != nil {
return nil, err
}
if dstRouteEntry == nil {
suspicions = append(suspicions, model.Suspicion{
Level: model.SuspicionLevelFatal,
Message: fmt.Sprintf("no route entry for destination ip %q", pkt.Dst),
})
}
if dstECS != "" && dstRouteEntry != nil && !slices.Contains(dstECSInfo.Network.IP, pkt.Dst.String()) {
// we do not route dst ip in ecs network ips
// is there any situation that len(NextHop) == 0?
nextHop := dstRouteEntry.NextHops.NextHop[0]
if *nextHop.NextHopType != "local" &&
!(*nextHop.NextHopType == "Instance" && *nextHop.NextHopId == dstECS) {
suspicions = append(suspicions, model.Suspicion{
Level: model.SuspicionLevelFatal,
Message: fmt.Sprintf("error route next hop for destination ip \"%s\", expect: \"Instance-%s\", actually: \"%s-%s\"",
pkt.Dst.String(), dstECS, *nextHop.NextHopType, *nextHop.NextHopId),
})
}
}
// reverse path
if srcECS != "" {
var srcRouteEntry *vpc.DescribeRouteEntryListResponseBodyRouteEntrysRouteEntry
if dstECS != "" {
if !slices.Contains(dstECSInfo.Network.IP, pkt.Dst.String()) {
routeEntries = dstECSInfo.Network.VpcDefaultRouteTableEntries
lo.ForEach(dstECSInfo.Network.NetworkInterfaces, func(ni *aliyun.ENIInfo, _ int) {
lo.ForEach(ni.NetworkInterfaceSet.PrivateIpSets.PrivateIpSet, func(ip *ecs.DescribeNetworkInterfacesResponseBodyNetworkInterfaceSetsNetworkInterfaceSetPrivateIpSetsPrivateIpSet, _ int) {
if ip.PrivateIpAddress != nil && *ip.PrivateIpAddress == pkt.Dst.String() {
routeEntries = ni.RouteTableEntries
}
})
})
} else {
routeEntries = dstECSInfo.Network.RouteTableEntries
}
} else {
routeEntries = srcECSInfo.Network.VpcDefaultRouteTableEntries
}
if privateIP != "" {
srcRouteEntry, err = routeMatchPacket(privateIP, routeEntries)
if err != nil {
return nil, err
}
} else {
srcRouteEntry, err = routeMatchPacket(pkt.Src.String(), routeEntries)
}
if err != nil {
return nil, err
}
if srcRouteEntry == nil {
suspicions = append(suspicions, model.Suspicion{
Level: model.SuspicionLevelFatal,
Message: fmt.Sprintf("no route entry for src ip %q", pkt.Src.String()),
})
}
if srcRouteEntry != nil && !slices.Contains(srcECSInfo.Network.IP, pkt.Src.String()) {
nextHop := srcRouteEntry.NextHops.NextHop[0]
if nextHop.NextHopRegionId != nil && *nextHop.NextHopRegionId != "local" &&
!(*nextHop.NextHopType == "Instance" && *nextHop.NextHopId == srcECS) {
suspicions = append(suspicions, model.Suspicion{
Level: model.SuspicionLevelFatal,
Message: fmt.Sprintf("error route next hop for source ip: %q, expect: \"Instance-%s\", actual: \"%s-%s\"",
pkt.Src, srcECSInfo.ID, *nextHop.NextHopType, *nextHop.NextHopId),
})
}
}
}
return suspicions, nil
}