Skip to content
Snippets Groups Projects
networkInstances_linux.go 4.76 KiB
Newer Older
  • Learn to ignore specific revisions
  • Shrey Garg's avatar
    Shrey Garg committed
    package additions
    
    import (
    	"fmt"
    	"net"
    
    
    	gnmitargetygot "code.fbi.h-da.de/danet/gnmi-target/examples/example01/model"
    
    	log "github.com/sirupsen/logrus"
    
    
    Shrey Garg's avatar
    Shrey Garg committed
    	"github.com/vishvananda/netlink"
    
    	"github.com/vishvananda/netlink/nl"
    
    Shrey Garg's avatar
    Shrey Garg committed
    )
    
    func (oc *networkInstances) SetStaticRoute(staticRoute *StaticRoute) error {
    	for _, hop := range staticRoute.Hops {
    
    		log.Info(*hop.InterfaceRef)
    
    Shrey Garg's avatar
    Shrey Garg committed
    		link, err := netlink.LinkByName(*hop.InterfaceRef)
    		if err != nil {
    
    Shrey Garg's avatar
    Shrey Garg committed
    		}
    
    
    		ipnet, err := cidrToIPNet(*staticRoute.Prefix)
    
    Shrey Garg's avatar
    Shrey Garg committed
    		if err != nil {
    			return err
    		}
    		nextHop, ok := hop.NextHop.(*gnmitargetygot.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop_Config_NextHop_Union_String)
    		if !ok {
    			return fmt.Errorf("only string union is supported for nextHop")
    		}
    
    		nextHopParsed := net.ParseIP(nextHop.String)
    
    		if nextHop == nil {
    			return fmt.Errorf("failed parsing ip")
    		}
    
    		route := &netlink.Route{
    			LinkIndex: link.Attrs().Index,
    			Dst:       ipnet,
    			Gw:        nextHopParsed,
    		}
    
    
    		log.Debug("Created netlink route: ", route.String())
    
    
    Shrey Garg's avatar
    Shrey Garg committed
    		if err := netlink.RouteReplace(route); err != nil {
    
    			log.Debug("Failed route replace: ", err)
    
    Shrey Garg's avatar
    Shrey Garg committed
    			return err
    		}
    	}
    
    	return nil
    }
    
    
    func (oc *networkInstances) GetStaticRoutes(linkName string) ([]*StaticRoute, error) {
    	// Create Interfaces
    	h, err := netlink.NewHandle()
    	if err != nil {
    		log.WithFields(log.Fields{}).Error(err)
    	}
    
    	link, err := h.LinkByName(linkName)
    	if err != nil {
    		return nil, err
    	}
    
    	localRoutesV4, err := h.RouteList(link, nl.FAMILY_V4)
    	if err != nil {
    		log.WithFields(log.Fields{}).Error(err)
    	}
    
    	localRoutesV6, err := h.RouteList(link, nl.FAMILY_V6)
    	if err != nil {
    		log.WithFields(log.Fields{}).Error(err)
    	}
    
    	staticRoutes := make([]*StaticRoute, 0)
    	for _, localRoute := range localRoutesV4 {
    		if localRoute.Gw != nil && localRoute.Src == nil {
    			staticRoutes = append(staticRoutes, staticRouteBasedOnFamily(link, localRoute, nl.FAMILY_V4))
    		}
    	}
    
    	for _, localRoute := range localRoutesV6 {
    		if localRoute.Gw != nil && localRoute.Src == nil {
    			staticRoutes = append(staticRoutes, staticRouteBasedOnFamily(link, localRoute, nl.FAMILY_V6))
    		}
    	}
    
    	return staticRoutes, nil
    }
    
    
    func (os *networkInstances) SubscribeToRoute() (chan *StaticRoute, error) {
    	// till now, when there is a new notif, it will get all of the network routes table
    	// todo: when get notif, search what has happened (add or update or delete)
    	// todo: only get the network routes table on that specific interfaces(link variable in netlink or code), because there will be more than 1 interfaces
    	// and if we update the all of routing table, it will hurt the performance
    	// todo: if possible change only the specific route either add, update or delete on that specific interface
    	staticRouteChannel := make(chan *StaticRoute)
    	routeChannel := make(chan netlink.RouteUpdate)
    	routeDone:= make(chan struct{})
    	if err := netlink.RouteSubscribe(routeChannel, routeDone); err != nil {
    		return nil, err
    	}
    
    	go func() {
    		for {
    			select {
    			case update := <- routeChannel:
    				log.Printf("received a route update for route: %s", update.Gw.String())
    				// staticRouteChannel <- &StaticRoute{}
    				if update.Route.LinkIndex > 0 {
    					link, err := netlink.LinkByIndex(update.Route.LinkIndex)
    					if err != nil {
    						fmt.Println("error getting link: ", err)
    						continue
    					}
    					if update.Route.Gw != nil && update.Route.Src == nil {
                            family := nl.FAMILY_V4
                            if update.Route.Gw.To4() == nil {
                                family = nl.FAMILY_V6
                            }
                            staticRoute := staticRouteBasedOnFamily(link, update.Route, family)
                            
                            staticRouteChannel <- staticRoute
                        }
    				} 
    			}
    		}
    	}()
    
    	return staticRouteChannel, nil
    }
    
    
    func staticRouteBasedOnFamily(link netlink.Link, route netlink.Route, family int) *StaticRoute {
    	prefix := ipNetIsNilConverter(route.Dst, family)
    	nextHop := &gnmitargetygot.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop_Config_NextHop_Union_String{String: route.Gw.String()}
    	index := fmt.Sprintf("AUTO_%s", nextHop.String)
    	return &StaticRoute{
    		Prefix: &prefix,
    		Hops: []Hop{
    			Hop{
    				InterfaceRef: &link.Attrs().Name,
    				Index:        &index,
    				NextHop:      nextHop,
    			},
    		},
    	}
    }
    
    func ipNetIsNilConverter(ipNet *net.IPNet, family int) string {
    	if ipNet != nil {
    		return ipNet.String()
    	}
    
    	switch family {
    	case nl.FAMILY_V4:
    		return "0.0.0.0/0"
    	case nl.FAMILY_V6:
    		return "::/0"
    	default:
    		return ipNet.String()
    	}
    }
    
    func cidrToIPNet(s string) (*net.IPNet, error) {
    
    Shrey Garg's avatar
    Shrey Garg committed
    	ip, ipnet, err := net.ParseCIDR(s)
    	if err != nil {
    		return nil, err
    	}
    	ipnet.IP = ip
    
    	return ipnet, nil