// 2020 Rackspace, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package netparse import ( "fmt" "net" "regexp" "github.com/sirupsen/logrus" ) var ( ipRouteRE = regexp.MustCompile(`(?i)ip\s+route\s+(.*)(\n|$)`) ipRouteVRFRE = regexp.MustCompile(`(?i)vrf\s+(\S+)`) ipRouteDestRE = regexp.MustCompile(`(?i)((\d+\.\d+\.\d+\.\d+\/\d{1,32})|(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+))\s+(.*)(\n|$)`) ipRouteNextHopRE = regexp.MustCompile(`(?i)(\d+\.\d+\.\d+\.\d+|null\d+)(\s+name\s+(\S+)|\s+\d+|)(\n|$)`) ) type ( // IPRoute defines a grouping of networks with a similar nexthop IP address. IPRoute struct { NextHop net.IP `json:"next_hop"` Dest []DestinationNetwork `json:"networks"` } // DestinationNetwork is a network/netmask combination for a given next-hop route. DestinationNetwork struct { Network net.IP `json:"network"` Netmask string `json:"netmask"` Interface string `json:"interface"` Name string `json:"name"` VRF string `json:"vrf"` } ) // IPRouteParse function is used to call the ip route parser and return a list of IPRoute // types. func IPRouteParse(s string) []IPRoute { retVal := make([]IPRoute, 0) routeMap := make(map[string][]DestinationNetwork) // ip route () for _, r := range ipRouteRE.FindAllStringSubmatch(s, -1) { val := DestinationNetwork{} val.VRF = func() string { if val := ipRouteVRFRE.FindStringSubmatch(r[1]); len(val) == 2 { return val[1] } return "" }() // If the network contains a slash, we are dealing with CIDR format, and // we need to handle this differently d := ipRouteDestRE.FindStringSubmatch(r[1]) nh := ipRouteNextHopRE.FindStringSubmatch(d[5]) if len(nh) < 4 { continue } val.Name = nh[4] if d[2] != "" { ip, nm, err := net.ParseCIDR(d[2]) if err != nil { logrus.Error(err) continue } val.Network = ip val.Netmask = ipv4MaskString(nm.Mask) } else { val.Network = net.ParseIP(d[3]) val.Netmask = d[4] } routeMap[nh[1]] = append(routeMap[nh[1]], val) } for val, key := range routeMap { retVal = append(retVal, IPRoute{NextHop: net.ParseIP(val), Dest: key}) } return retVal } // method to call IPRoutesGetGateway func RoutesWithNextHop(nextHopAddr string, routes []IPRoute) []IPRoute { retVal := make([]IPRoute, 0) for _, r := range routes { if r.NextHop.String() == nextHopAddr { retVal = append(retVal, r) } } return retVal } // CIDR will convert the network net.IP object, and the subnet string into // a string CIDR func (d *DestinationNetwork) CIDR() string { nm := net.ParseIP(d.Netmask).To4() ones, _ := net.IPv4Mask(nm[0], nm[1], nm[2], nm[3]).Size() return fmt.Sprintf("%s/%d", d.Network.String(), ones) } func ipv4MaskString(m []byte) string { if len(m) != 4 { return "" } return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) }