pkg/skoop/netstack/route.go (264 lines of code) (raw):

package netstack import ( "fmt" "net" "sort" "strings" "github.com/alibaba/kubeskoop/pkg/skoop/model" "github.com/pkg/errors" "golang.org/x/sys/unix" ) type Scope uint8 const ( FamilyAll = unix.AF_UNSPEC FamilyV4 = unix.AF_INET FamilyV6 = unix.AF_INET6 RtTableLocal = 0xff RtTableDefault = 0xfd RtTableMain = 0xfe ScopeUniverse Scope = 0x0 ScopeSite Scope = 0xc8 ScopeLink Scope = 0xfd ScopeHost Scope = 0xfe ScopeNowhere Scope = 0xff RTProtBIRD = 0xc RTProtBoot = 0x3 RTProtKernel = 0x2 RTProtOSPF = 0xbc RTProtRA = 0x9 RTProtRedirect = 0x1 RTProtRIP = 0xbd RTProtStatic = 0x4 ) var ErrNoRouteToHost = errors.New("no route to host") type Route struct { Family int `json:"f"` OifName string `json:"o"` IifName string `json:"i"` Scope Scope `json:"sc"` Dst *net.IPNet `json:"d"` Src net.IP `json:"s"` Gw net.IP `json:"g"` Protocol int `json:"p"` Priority int `json:"pr"` Table int `json:"tb"` Type int `json:"t"` Tos int `json:"tos"` Flags int `json:"fl"` } func (r Route) String() string { var formattedString []string if r.Dst != nil { formattedString = append(formattedString, r.Dst.String()) } else { formattedString = append(formattedString, "default") } if r.OifName != "" { formattedString = append(formattedString, fmt.Sprintf("dev %s", r.OifName)) } if r.Gw != nil && !r.Gw.IsUnspecified() { formattedString = append(formattedString, fmt.Sprintf("via %s", r.Gw)) } if scope := RouteScopeToString(r.Scope); scope != "" { formattedString = append(formattedString, fmt.Sprintf("scope %s", scope)) } if routeType := RouteTypeToString(r.Type); routeType != "" { formattedString = append(formattedString, fmt.Sprintf("type %s", routeType)) } // todo: prefsrc return strings.Join(formattedString, " ") } func RouteTypeToString(routeType int) string { switch routeType { case RtnLocal: return "local" case RtnUnicast: return "unicast" case RtnBroadcast: return "broadcast" case RtnAnycast: return "anycast" case RtnUnreachable: return "unreachable" case RtnBlackhole: return "blackhole" case RtnProhibit: return "prohibit" } return "" } func RouteScopeToString(scope Scope) string { switch scope { case ScopeLink: return "link" case ScopeHost: return "host" case ScopeUniverse: return "universe" } return "" } func RouteProtocolToString(protocol int) string { switch protocol { case RTProtRedirect: return "redirect" case RTProtKernel: return "kernel" case RTProtBoot: return "boot" case RTProtStatic: return "static" case RTProtRA: return "ra" case RTProtOSPF: return "ospf" case RTProtRIP: return "rip" case RTProtBIRD: return "bird" } return "" } type Rule struct { Priority int `json:"p"` Family int `json:"f"` Table int `json:"tb"` Mark int `json:"m"` Mask int `json:"ma"` Tos uint `json:"tos"` TunID uint `json:"ti"` Goto int `json:"gt"` Src *net.IPNet `json:"s"` Dst *net.IPNet `json:"d"` Flow int `json:"fl"` IifName string `json:"i"` OifName string `json:"o"` } type Router interface { RouteSrc(packet *model.Packet, iif, oif string) (string, *Route, error) Route(packet *model.Packet, iif, oif string) (*Route, error) TableRoute(tableID int, packet *model.Packet) (*Route, error) DefaultRoute(table int) *Route } type SimulateRouter struct { rules []Rule routes map[int][]Route interfaces []Interface } func sortRule(rules []Rule) { sort.SliceStable(rules, func(i, j int) bool { return rules[i].Priority < rules[j].Priority }) } func sortRoute(routes []Route) { sort.SliceStable(routes, func(i, j int) bool { leftOnes, _ := routes[i].Dst.Mask.Size() rightOnes, _ := routes[j].Dst.Mask.Size() if leftOnes == rightOnes { return routes[i].Priority < routes[j].Priority } return leftOnes > rightOnes }) } func matchRule(packet *model.Packet, rule Rule, iif, oif string) bool { if rule.Src != nil && !rule.Src.Contains(packet.Src) { return false } if rule.Dst != nil && !rule.Dst.Contains(packet.Dst) { return false } if rule.Mark > 0 && (int(packet.Mark)^rule.Mark)&rule.Mask != 0 { return false } if rule.IifName != "" && rule.IifName != iif { return false } if rule.OifName != "" && rule.OifName != oif { return false } return true } func NewSimulateRouter(rules []Rule, routes []Route, interfaces []Interface) *SimulateRouter { router := &SimulateRouter{ rules: rules, routes: make(map[int][]Route), interfaces: interfaces, } sortRule(router.rules) routeTableMap := make(map[int][]Route) for _, route := range routes { routeTableMap[route.Table] = append(routeTableMap[route.Table], route) } for table, routes := range routeTableMap { sortRoute(routes) router.routes[table] = routes } return router } func (r *SimulateRouter) lookupRoute(table int, packet *model.Packet) *Route { routes, ok := r.routes[table] if !ok { return nil } for _, route := range routes { //不用判断nil,构造route的时候把Dst设置好 if route.Dst.Contains(packet.Dst) { return &route } } return nil } func (r *SimulateRouter) Route(packet *model.Packet, iif, oif string) (*Route, error) { for _, rule := range r.rules { if matchRule(packet, rule, iif, oif) { route := r.lookupRoute(rule.Table, packet) if route != nil { return route, nil } } } return nil, ErrNoRouteToHost } func (r *SimulateRouter) DefaultRoute(table int) *Route { if table == 0 { table = RtTableLocal } for _, rt := range r.routes[table] { if rt.Dst == nil || rt.Dst.String() == "0.0.0.0/0" { return &rt } } return nil } func (r *SimulateRouter) TableRoute(table int, packet *model.Packet) (*Route, error) { route := r.lookupRoute(table, packet) if route == nil { return nil, ErrNoRouteToHost } return route, nil } func (r *SimulateRouter) RouteSrc(packet *model.Packet, iif, oif string) (string, *Route, error) { route, err := r.Route(packet, iif, oif) if err != nil { return "", nil, err } interfaceAddr := func(rt *Route) string { for _, oif := range r.interfaces { if oif.Name == rt.OifName { for _, addr := range oif.Addrs { if addr.IP.To4() != nil { return addr.IP.String() } } } } return "" } dstRouteSrc := interfaceAddr(route) if dstRouteSrc == "" { // use default route interface addr route = r.DefaultRoute(RtTableMain) dstRouteSrc = interfaceAddr(route) } if dstRouteSrc == "" { return "", nil, fmt.Errorf("cannot found valid ip for dst %v route", packet.Dst) } return dstRouteSrc, route, nil }