diff --git a/drivers/ipvlan/ipvlan.go b/drivers/ipvlan/ipvlan.go index 8ea44fcbb4..d31dd0e3ca 100644 --- a/drivers/ipvlan/ipvlan.go +++ b/drivers/ipvlan/ipvlan.go @@ -15,11 +15,16 @@ const ( vethLen = 7 containerVethPrefix = "eth" vethPrefix = "veth" - ipvlanType = "ipvlan" // driver type name - modeL2 = "l2" // ipvlan mode l2 is the default - modeL3 = "l3" // ipvlan L3 mode - parentOpt = "parent" // parent interface -o parent - modeOpt = "_mode" // ipvlan mode ux opt suffix + ipvlanType = "ipvlan" // driver type name + modeL2 = "l2" // ipvlan mode l2 is the default + modeL3 = "l3" // ipvlan L3 mode + parentOpt = "parent" // parent interface -o parent + modeOpt = "_mode" // ipvlan mode ux opt suffix + bgpNeighborOpt = "bgp-neighbor" // BGP neighbor address + vrfOpt = "vrf" // BGP vrf ID + asOpt = "asnum" // BGP AS number default 65000 + remoteAsOpt = "rasnum" // BGP remote AS number dafault 65000 + subnetAdvertise = "subnet-advertise" // Advertise IP Subnet with BGP ) var driverModeOpt = ipvlanType + modeOpt // mode -o ipvlan_mode diff --git a/drivers/ipvlan/ipvlan_joinleave.go b/drivers/ipvlan/ipvlan_joinleave.go index 771f126d0b..ac23115553 100644 --- a/drivers/ipvlan/ipvlan_joinleave.go +++ b/drivers/ipvlan/ipvlan_joinleave.go @@ -59,6 +59,14 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, if err := jinfo.AddStaticRoute(defaultRoute.Destination, defaultRoute.RouteType, defaultRoute.NextHop); err != nil { return fmt.Errorf("failed to set an ipvlan l3 mode ipv4 default gateway: %v", err) } + if n.config.SubnetAdvertise == "" { + //Advertise container route as /32 route + advIP := &net.IPNet{IP: ep.addr.IP, Mask: net.IPv4Mask(255, 255, 255, 255)} + err = routemanager.AdvertiseNewRoute(advIP.String(), n.config.VrfID) + if err != nil { + return err + } + } logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Ipvlan_Mode: %s, Parent: %s", ep.addr.IP.String(), n.config.IpvlanMode, n.config.Parent) // If the endpoint has a v6 address, set a v6 default route @@ -130,6 +138,11 @@ func (d *driver) Leave(nid, eid string) error { if err != nil { return err } + if network.config.IpvlanMode == modeL3 && network.config.SubnetAdvertise == "" { + //Withdraw container route as /32 route + witdIP := &net.IPNet{IP: endpoint.addr.IP, Mask: net.IPv4Mask(255, 255, 255, 255)} + err = routemanager.WithdrawRoute(witdIP.String(), network.config.VrfID) + } if endpoint == nil { return fmt.Errorf("could not find endpoint with id %s", eid) } diff --git a/drivers/ipvlan/ipvlan_network.go b/drivers/ipvlan/ipvlan_network.go index 7a58a382d0..09df467e5f 100644 --- a/drivers/ipvlan/ipvlan_network.go +++ b/drivers/ipvlan/ipvlan_network.go @@ -2,7 +2,6 @@ package ipvlan import ( "fmt" - "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/stringid" @@ -11,6 +10,7 @@ import ( "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" + "strconv" ) // CreateNetwork the network for the specified driver type @@ -113,6 +113,34 @@ func (d *driver) createNetwork(config *configuration) error { endpoints: endpointTable{}, config: config, } + if config.IpvlanMode == modeL3 { + if routemanager == nil { + InitRouteMonitering(config.ASnum, config.RemoteASnum) + } + err := routemanager.CreateVrfNetwork(config.Parent, config.VrfID) + if err != nil { + return err + } + if config.BgpNeighbor != "" { + routemanager.DiscoverNew(false, config.BgpNeighbor) + } + if config.SubnetAdvertise != "" { + if config.Ipv4Subnets != nil { + for _, subnet := range config.Ipv4Subnets { + err := routemanager.AdvertiseNewRoute(subnet.SubnetIP, config.VrfID) + if err != nil { + return err + } + } + for _, subnet := range config.Ipv6Subnets { + err := routemanager.AdvertiseNewRoute(subnet.SubnetIP, config.VrfID) + if err != nil { + return err + } + } + } + } + } // add the *network d.addNetwork(n) @@ -147,6 +175,23 @@ func (d *driver) DeleteNetwork(nid string) error { } } } + if n.config.SubnetAdvertise != "" { + //Advertise container network subnet + if n.config.Ipv4Subnets != nil { + for _, subnet := range n.config.Ipv4Subnets { + err := routemanager.WithdrawRoute(subnet.SubnetIP, n.config.VrfID) + if err != nil { + return err + } + } + for _, subnet := range n.config.Ipv6Subnets { + err := routemanager.WithdrawRoute(subnet.SubnetIP, n.config.VrfID) + if err != nil { + return err + } + } + } + } // delete the *network d.deleteNetwork(nid) // delete the network record from persistent cache @@ -211,6 +256,26 @@ func (config *configuration) fromOptions(labels map[string]string) error { case driverModeOpt: // parse driver option '-o ipvlan_mode' config.IpvlanMode = value + case bgpNeighborOpt: + // parse driver option '-o bgp-neighbor' + config.BgpNeighbor = value + case vrfOpt: + // parse driver option '-o vrf' + _, err := strconv.Atoi(value) + if err != nil { + logrus.Errorf("vrf ID must be numeral") + return err + } + config.VrfID = value + case asOpt: + // parse driver options '-o asnum' + config.ASnum = value + case remoteAsOpt: + // parse driver options '-o rasnum' + config.RemoteASnum = value + case subnetAdvertise: + // set driver options '-o subnet-advertise' + config.SubnetAdvertise = "True" } } return nil diff --git a/drivers/ipvlan/ipvlan_store.go b/drivers/ipvlan/ipvlan_store.go index c6430835ae..8522e4245f 100644 --- a/drivers/ipvlan/ipvlan_store.go +++ b/drivers/ipvlan/ipvlan_store.go @@ -25,6 +25,11 @@ type configuration struct { CreatedSlaveLink bool Ipv4Subnets []*ipv4Subnet Ipv6Subnets []*ipv6Subnet + BgpNeighbor string + VrfID string + ASnum string + RemoteASnum string + SubnetAdvertise string } type ipv4Subnet struct { diff --git a/drivers/ipvlan/l3_del_routes.go b/drivers/ipvlan/l3_del_routes.go new file mode 100644 index 0000000000..7c5beb1d39 --- /dev/null +++ b/drivers/ipvlan/l3_del_routes.go @@ -0,0 +1,122 @@ +package ipvlan + +import ( + "fmt" + log "github.com/Sirupsen/logrus" + "github.com/vishvananda/netlink" + "net" +) + +// Cleanup links with netlink syscalls with a scope of: +// RT_SCOPE_LINK = 0xfd (253) +// RT_SCOPE_UNIVERSE = 0x0 (0) +func cleanExistingRoutes(ifaceStr string) error { + iface, err := netlink.LinkByName(ifaceStr) + ipvlanParentIface, err := netlink.LinkByName(ifaceStr) + if err != nil { + log.Errorf("Error occoured finding the linux link [ %s ] from netlink: %s", ipvlanParentIface.Attrs().Name, err) + return err + } + routes, err := netlink.RouteList(iface, netlink.FAMILY_V4) + if err != nil { + log.Errorf("Unable to retreive netlink routes: %s", err) + return err + } + ifaceIP, err := getIfaceIP(ifaceStr) + if err != nil { + log.Errorf("Unable to retreive a usable IP via ethernet interface: %s", ifaceStr) + return err + } + for _, route := range routes { + if route.Dst == nil { + log.Debugf("Ignoring route [ %v ] Dst is nil", route) + continue + } + if netOverlaps(ifaceIP, route.Dst) == true { + log.Debugf("Ignoring route [ %v ] as it is associated to the [ %s ] interface", ifaceIP, ifaceStr) + } else if route.Scope == 0x0 || route.Scope == 0xfd { + // Remove link and universal routes from the docker host ipvlan interface + log.Debugf("Cleaning static route cache for the destination: [ %s ]", route.Dst) + err := delRoute(route, ipvlanParentIface) + if err != nil { + log.Errorf("Error deleting static route cache for Destination: [ %s ] and Nexthop [ %s ] Error: %s", route.Dst, route.Gw, err) + } + } + } + return nil +} + +func verifyRoute(bgpRoute *net.IPNet) { + networks, err := netlink.RouteList(nil, netlink.FAMILY_V4) + if err != nil { + return + } + for _, network := range networks { + if network.Dst != nil && netOverlaps(bgpRoute, network.Dst) { + log.Errorf("The network [ %v ] learned via BGP conflicts with an existing route on this host [ %v ]", bgpRoute, network.Dst) + return + } + } + return +} + +func netOverlaps(netX *net.IPNet, netY *net.IPNet) bool { + if firstIP, _ := networkRange(netX); netY.Contains(firstIP) { + return true + } + if firstIP, _ := networkRange(netY); netX.Contains(firstIP) { + return true + } + return false +} + +func networkRange(network *net.IPNet) (net.IP, net.IP) { + var ( + netIP = network.IP.To4() + firstIP = netIP.Mask(network.Mask) + lastIP = net.IPv4(0, 0, 0, 0).To4() + ) + + for i := 0; i < len(lastIP); i++ { + lastIP[i] = netIP[i] | ^network.Mask[i] + } + return firstIP, lastIP +} + +func getIfaceIP(name string) (*net.IPNet, error) { + iface, err := netlink.LinkByName(name) + if err != nil { + return nil, err + } + addrs, err := netlink.AddrList(iface, netlink.FAMILY_V4) + if err != nil { + return nil, err + } + if len(addrs) == 0 { + return nil, fmt.Errorf("Interface %v has no IP addresses", name) + } + if len(addrs) > 1 { + log.Debugf("Interface %v has more than 1 IPv4 address. Default is %v\n", name, addrs[0].IP) + } + return addrs[0].IPNet, nil +} + +// delRoute deletes any netlink route +func delRoute(route netlink.Route, iface netlink.Link) error { + return netlink.RouteDel(&netlink.Route{ + Scope: route.Scope, + LinkIndex: iface.Attrs().Index, + Dst: route.Dst, + Gw: route.Gw, + }) +} + +// delRemoteRoute deletes a host-scoped route to a device. +func delRemoteRoute(neighborNetwork *net.IPNet, nextHop net.IP, iface netlink.Link) error { + return netlink.RouteDel(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: iface.Attrs().Index, + Dst: neighborNetwork, + Gw: nextHop, + }) +} diff --git a/drivers/ipvlan/l3_route_common.go b/drivers/ipvlan/l3_route_common.go new file mode 100644 index 0000000000..0eccc165b5 --- /dev/null +++ b/drivers/ipvlan/l3_route_common.go @@ -0,0 +1,27 @@ +package ipvlan + +import ( + "net" +) + +type ribCache struct { + BgpTable map[string]*ribLocal +} + +// Unmarshalled BGP update binding for simplicity +type ribLocal struct { + BgpPrefix *net.IPNet + OriginatorIP net.IP + NextHop net.IP + Age int + Best bool + IsWithdraw bool + IsHostRoute bool + IsLocal bool + AsPath string + RT string +} + +type ribTest struct { + BgpTable map[string]*ribLocal +} diff --git a/drivers/ipvlan/l3_route_interface.go b/drivers/ipvlan/l3_route_interface.go new file mode 100644 index 0000000000..c1e2c069e1 --- /dev/null +++ b/drivers/ipvlan/l3_route_interface.go @@ -0,0 +1,21 @@ +package ipvlan + +var routemanager routingInterface + +type host struct { + isself bool + Address string +} + +type routingInterface interface { + CreateVrfNetwork(ParentIface string, vrfID string) error + AdvertiseNewRoute(localPrefix string, vrfID string) error + WithdrawRoute(localPrefix string, vrfID string) error + DiscoverNew(isself bool, Address string) error + DiscoverDelete(isself bool, Address string) error +} + +// InitRouteMonitering initialize and start maniternig routing table of host +func InitRouteMonitering(as string, ras string) { + routemanager = NewBgpRouteManager(as, ras) +} diff --git a/drivers/ipvlan/l3_route_manager.go b/drivers/ipvlan/l3_route_manager.go new file mode 100644 index 0000000000..30349d9e34 --- /dev/null +++ b/drivers/ipvlan/l3_route_manager.go @@ -0,0 +1,496 @@ +package ipvlan + +import ( + "fmt" + "net" + + log "github.com/Sirupsen/logrus" + bgpapi "github.com/osrg/gobgp/libapi" + bgp "github.com/osrg/gobgp/packet/bgp" + bgpserver "github.com/osrg/gobgp/server" + "strconv" + "strings" +) + +const ( + bgpVrfprefix = "vrf" +) + +type rib struct { + path *bgpapi.Path + vrfID string +} + +// BgpRouteManager advertize and withdraw routes by BGP +type BgpRouteManager struct { + // Master interface for IPVlan and BGP peering source + parentIfaces map[string]string + bgpServer *bgpserver.BgpServer + learnedRoutes map[string]*ribLocal + bgpGlobalcfg *bgpapi.Global + asnum int + rasnum int + neighborlist []string +} + +// NewBgpRouteManager initialize route manager +func NewBgpRouteManager(as string, ras string) *BgpRouteManager { + a, err := strconv.Atoi(as) + if err != nil { + log.Debugf("AS number is not numeral or empty %s, using default AS num: 65000", as) + a = 65000 + } + ra, err := strconv.Atoi(ras) + if err != nil { + log.Debugf("Remote AS number is not numeral or empty %s, using default remote AS num: 65000", as) + ra = 65000 + } + b := &BgpRouteManager{ + parentIfaces: make(map[string]string), + asnum: a, + rasnum: ra, + learnedRoutes: make(map[string]*ribLocal), + } + b.bgpServer = bgpserver.NewBgpServer() + go b.bgpServer.Serve() + return b +} + +//CreateVrfNetwork create vrf in BGP server and start monitoring vrf rib if vrfID="", network use global rib +func (b *BgpRouteManager) CreateVrfNetwork(parentIface string, vrfID string) error { + log.Debugf("BGP Global config : %v", b.bgpGlobalcfg) + if b.bgpGlobalcfg == nil { + log.Debugf("BGP Global config is emply set config") + b.parentIfaces["global"] = parentIface + iface, _ := net.InterfaceByName(parentIface) + addrs, _ := iface.Addrs() + for _, a := range addrs { + ip, _, err := net.ParseCIDR(a.String()) + if err != nil { + log.Errorf("error deriving ip-address in bind interface (%s) : %v", parentIface, err) + return err + } + if ip.To4() == nil || ip.IsLoopback() { + continue + } + RouterID := ip.String() + b.SetBgpConfig(RouterID) + break + } + } + if vrfID == "" { + log.Debugf("vrf ID is empty. network paths are in global rib") + return nil + } + b.parentIfaces[vrfID] = parentIface + err := cleanExistingRoutes(parentIface) + if err != nil { + log.Debugf("Error cleaning old routes: %s", err) + return err + } + rdrtstr := strconv.Itoa(b.asnum) + ":" + vrfID + rd, err := bgp.ParseRouteDistinguisher(rdrtstr) + if err != nil { + log.Errorf("Fail to parse RD from vrfID %s : %s", vrfID, err) + return err + } + rdSerialized, _ := rd.Serialize() + + rt, err := bgp.ParseRouteTarget(rdrtstr) + if err != nil { + log.Errorf("Fail to parse RT from vrfID %s : %s", vrfID, err) + return err + } + rtSerialized, _ := rt.Serialize() + var rts [][]byte + rts = append(rts, rtSerialized) + + arg := &bgpapi.Vrf{ + Name: bgpVrfprefix + vrfID, + Rd: rdSerialized, + ImportRt: rts, + ExportRt: rts, + } + err = b.bgpServer.AddVrf(arg) + if err != nil { + return err + } + go func() { + err := b.monitorBestPath(vrfID) + log.Errorf("faital monitoring VrfID %s rib : %v", vrfID, err) + }() + return nil +} + +//SetBgpConfig set routet id +func (b *BgpRouteManager) SetBgpConfig(RouterID string) error { + b.bgpGlobalcfg = &bgpapi.Global{As: uint32(b.asnum), RouterId: RouterID} + err := b.bgpServer.StartServer(&bgpapi.StartServerRequest{ + Global: b.bgpGlobalcfg, + }) + if err != nil { + return err + } + go func() { + err := b.monitorBestPath("") + log.Errorf("faital monitoring global rib : %v", err) + }() + log.Debugf("Set BGP Global config: as %d, router id %v", b.asnum, RouterID) + return nil +} + +func (b *BgpRouteManager) handleRibUpdate(p *rib) error { + bgpCache := &ribCache{ + BgpTable: make(map[string]*ribLocal), + } + monitorUpdate, err := bgpCache.handleBgpRibMonitor(p.path, p.vrfID) + if err != nil { + log.Errorf("error processing bgp update [ %s ]", err) + return err + } else if monitorUpdate == nil { + return nil + } + if monitorUpdate.IsLocal != true { + if p.path.IsWithdraw { + monitorUpdate.IsWithdraw = true + log.Debugf("BGP update has withdrawn the routes: %v ", monitorUpdate) + if route, ok := b.learnedRoutes[monitorUpdate.BgpPrefix.String()]; ok { + err = delNetlinkRoute(route.BgpPrefix, route.NextHop, b.parentIfaces[p.vrfID]) + // If the bgp update contained a withdraw, remove the local netlink route for the remote endpoint + if err != nil { + log.Errorf("Error removing learned bgp route [ %s ]", err) + return err + } + delete(b.learnedRoutes, monitorUpdate.BgpPrefix.String()) + } else { + log.Debugf("Update withdrawn route has Unknown BGP prefix %s", monitorUpdate.BgpPrefix.String()) + return nil + } + } else { + monitorUpdate.IsWithdraw = false + b.learnedRoutes[monitorUpdate.BgpPrefix.String()] = monitorUpdate + log.Debugf("Learned routes: %v ", monitorUpdate) + + err = addNetlinkRoute(monitorUpdate.BgpPrefix, monitorUpdate.NextHop, b.parentIfaces[p.vrfID]) + if err != nil { + log.Debugf("Error Adding route results [ %s ]", err) + return err + } + log.Debugf("Updated the local prefix cache from the newly learned BGP update:") + for n, entry := range b.learnedRoutes { + log.Debugf("%s - %+v", n, entry) + } + } + } + log.Debugf("Verbose update details: %s", monitorUpdate) + return nil +} + +func (b *BgpRouteManager) monitorBestPath(VrfID string) error { + var routeFamily uint32 + if VrfID == "" { + routeFamily = uint32(bgp.RF_IPv4_UC) + VrfID = "global" + } else { + routeFamily = uint32(bgp.RF_IPv4_VPN) + } + err := func() error { + currib, err := b.bgpServer.GetRib(&bgpapi.Table{Type: bgpapi.Resource_GLOBAL, Family: routeFamily}) + if err != nil { + return err + } + for _, d := range currib.Destinations { + for _, p := range d.Paths { + if p.Best { + b.handleRibUpdate(&rib{path: p, vrfID: VrfID}) + break + } + } + } + return nil + }() + + if err != nil { + return err + } + ribCh, EndCh, err := b.bgpServer.MonitorRib(&bgpapi.Table{Type: bgpapi.Resource_ADJ_IN, Family: routeFamily}) + if err != nil { + return err + } + for r := range ribCh { + if err := r.Err(); err != nil { + log.Debug(err.Error()) + EndCh <- struct{}{} + return err + } + err := b.handleRibUpdate(&rib{path: r.Data.(*bgpapi.Destination).Paths[0], vrfID: VrfID}) + if err != nil { + log.Errorf(err.Error()) + continue + } + } + return nil +} + +// AdvertiseNewRoute advetise the local namespace IP prefixes to the bgp neighbors +func (b *BgpRouteManager) AdvertiseNewRoute(localPrefix string, VrfID string) error { + _, localPrefixCIDR, _ := net.ParseCIDR(localPrefix) + log.Debugf("Adding this hosts container network [ %s ] into the BGP domain", localPrefix) + path := &bgpapi.Path{ + Pattrs: make([][]byte, 0), + IsWithdraw: false, + } + var target bgpapi.Resource + if VrfID == "" { + target = bgpapi.Resource_GLOBAL + } else { + target = bgpapi.Resource_VRF + } + localPrefixMask, _ := localPrefixCIDR.Mask.Size() + path.Nlri, _ = bgp.NewIPAddrPrefix(uint8(localPrefixMask), localPrefixCIDR.IP.String()).Serialize() + n, _ := bgp.NewPathAttributeNextHop("0.0.0.0").Serialize() + path.Pattrs = append(path.Pattrs, n) + origin, _ := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP).Serialize() + path.Pattrs = append(path.Pattrs, origin) + arg := &bgpapi.AddPathRequest{ + Resource: target, + VrfId: bgpVrfprefix + VrfID, + Path: path, + } + _, err := b.bgpServer.AddPath(arg) + if err != nil { + return err + } + return nil +} + +//WithdrawRoute withdraw the local namespace IP prefixes to the bgp neighbors +func (b *BgpRouteManager) WithdrawRoute(localPrefix string, VrfID string) error { + _, localPrefixCIDR, _ := net.ParseCIDR(localPrefix) + log.Debugf("Withdraw this hosts container network [ %s ] from the BGP domain", localPrefix) + path := &bgpapi.Path{ + Pattrs: make([][]byte, 0), + IsWithdraw: true, + } + var target bgpapi.Resource + if VrfID == "" { + target = bgpapi.Resource_GLOBAL + } else { + target = bgpapi.Resource_VRF + } + localPrefixMask, _ := localPrefixCIDR.Mask.Size() + path.Nlri, _ = bgp.NewIPAddrPrefix(uint8(localPrefixMask), localPrefixCIDR.IP.String()).Serialize() + n, _ := bgp.NewPathAttributeNextHop("0.0.0.0").Serialize() + path.Pattrs = append(path.Pattrs, n) + origin, _ := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP).Serialize() + path.Pattrs = append(path.Pattrs, origin) + arg := &bgpapi.DeletePathRequest{ + Resource: target, + VrfId: bgpVrfprefix + VrfID, + Path: path, + } + err := b.bgpServer.DeletePath(arg) + if err != nil { + return err + } + return nil +} + +//ModPeer add or delete bgp peer : oreration add - true, del - fasle +func (b *BgpRouteManager) ModPeer(peeraddr string, operation bool) error { + peer := &bgpapi.Peer{ + Conf: &bgpapi.PeerConf{ + NeighborAddress: peeraddr, + PeerAs: uint32(b.rasnum), + }, + Families: []uint32{uint32(bgp.RF_IPv4_UC), uint32(bgp.RF_IPv6_UC), uint32(bgp.RF_IPv4_VPN), uint32(bgp.RF_IPv6_VPN)}, + } + if operation { + if err := b.bgpServer.AddNeighbor(peer); err != nil { + return err + } + } else { + if err := b.bgpServer.DeleteNeighbor(peer); err != nil { + return err + } + } + return nil +} + +// DiscoverNew host discovery for add new peer +func (b *BgpRouteManager) DiscoverNew(isself bool, Address string) error { + if isself { + error := b.SetBgpConfig(Address) + if error != nil { + return error + } + for _, nAddr := range b.neighborlist { + log.Debugf("BGP neighbor add %s", nAddr) + error := b.ModPeer(nAddr, true) + if error != nil { + return error + } + } + } else { + if b.bgpGlobalcfg != nil { + log.Debugf("BGP neighbor add %s", Address) + error := b.ModPeer(Address, true) + if error != nil { + return error + } + } + b.neighborlist = append(b.neighborlist, Address) + } + return nil +} + +//DiscoverDelete host discovery for delete +func (b *BgpRouteManager) DiscoverDelete(isself bool, Address string) error { + if isself { + return nil + } + if b.bgpGlobalcfg != nil { + log.Debugf("BGP neighbor del %s", Address) + error := b.ModPeer(Address, false) + if error != nil { + return error + } + } + + return nil +} +func (cache *ribCache) handleBgpRibMonitor(routeMonitor *bgpapi.Path, VrfID string) (*ribLocal, error) { + ribLocal := &ribLocal{} + var nlri bgp.AddrPrefixInterface + + if len(routeMonitor.Nlri) > 0 { + if VrfID == "global" { + nlri = &bgp.IPAddrPrefix{} + err := nlri.DecodeFromBytes(routeMonitor.Nlri) + if err != nil { + log.Errorf("Error parsing the bgp update nlri") + return nil, err + } + bgpPrefix, err := parseIPNet(nlri.String()) + if err != nil { + log.Errorf("Error parsing the bgp update prefix: %s", nlri.String()) + return nil, err + } + ribLocal.BgpPrefix = bgpPrefix + + } else { + nlri = bgp.NewLabeledVPNIPAddrPrefix(24, "", *bgp.NewMPLSLabelStack(), nil) + err := nlri.DecodeFromBytes(routeMonitor.Nlri) + if err != nil { + log.Errorf("Error parsing the bgp update nlri") + return nil, err + } + nlriSplit := strings.Split(nlri.String(), ":") + if VrfID != nlriSplit[1] { + return nil, nil + } + bgpPrefix, err := parseIPNet(nlriSplit[len(nlriSplit)-1]) + if err != nil { + log.Errorf("Error parsing the bgp update vpn prefix: %s", nlriSplit[len(nlriSplit)-1]) + return nil, err + } + ribLocal.BgpPrefix = bgpPrefix + + } + } + log.Debugf("BGP update for prefix: [ %s ] ", nlri.String()) + for _, attr := range routeMonitor.Pattrs { + p, err := bgp.GetPathAttribute(attr) + if err != nil { + log.Errorf("Error parsing the bgp update attr") + return nil, err + } + err = p.DecodeFromBytes(attr) + if err != nil { + log.Errorf("Error parsing the bgp update attr") + return nil, err + } + log.Debugf("Type: [ %d ] ,Value [ %s ]", p.GetType(), p.String()) + switch p.GetType() { + case bgp.BGP_ATTR_TYPE_ORIGIN: + // 0 = iBGP; 1 = eBGP + if p.(*bgp.PathAttributeOrigin).Value != nil { + log.Debugf("Type Code: [ %d ] Origin: %s", bgp.BGP_ATTR_TYPE_ORIGIN, p.(*bgp.PathAttributeOrigin).String()) + } + case bgp.BGP_ATTR_TYPE_AS_PATH: + if p.(*bgp.PathAttributeAsPath).Value != nil { + log.Debugf("Type Code: [ %d ] AS_Path: %s", bgp.BGP_ATTR_TYPE_AS_PATH, p.String()) + } + case bgp.BGP_ATTR_TYPE_NEXT_HOP: + if p.(*bgp.PathAttributeNextHop).Value.String() != "" { + log.Debugf("Type Code: [ %d ] Nexthop: %s", bgp.BGP_ATTR_TYPE_NEXT_HOP, p.String()) + n := p.(*bgp.PathAttributeNextHop) + ribLocal.NextHop = n.Value + if ribLocal.NextHop.String() == "0.0.0.0" { + ribLocal.IsLocal = true + } + } + case bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC: + if p.(*bgp.PathAttributeMultiExitDisc).Value >= 0 { + log.Debugf("Type Code: [ %d ] MED: %g", bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC, p.String()) + } + case bgp.BGP_ATTR_TYPE_LOCAL_PREF: + if p.(*bgp.PathAttributeLocalPref).Value >= 0 { + log.Debugf("Type Code: [ %d ] Local Pref: %g", bgp.BGP_ATTR_TYPE_LOCAL_PREF, p.String()) + } + case bgp.BGP_ATTR_TYPE_ORIGINATOR_ID: + if p.(*bgp.PathAttributeOriginatorId).Value != nil { + log.Debugf("Type Code: [ %d ] Originator IP: %s", bgp.BGP_ATTR_TYPE_ORIGINATOR_ID, p.String()) + ribLocal.OriginatorIP = p.(*bgp.PathAttributeOriginatorId).Value + log.Debugf("Type Code: [ %d ] Originator IP: %s", bgp.BGP_ATTR_TYPE_ORIGINATOR_ID, ribLocal.OriginatorIP) + } + case bgp.BGP_ATTR_TYPE_CLUSTER_LIST: + if len(p.(*bgp.PathAttributeClusterList).Value) > 0 { + log.Debugf("Type Code: [ %d ] Cluster List: %s", bgp.BGP_ATTR_TYPE_CLUSTER_LIST, p.String()) + } + case bgp.BGP_ATTR_TYPE_MP_REACH_NLRI: + if p.(*bgp.PathAttributeMpReachNLRI).Value != nil { + log.Debugf("Type Code: [ %d ] MP Reachable: %s", bgp.BGP_ATTR_TYPE_MP_REACH_NLRI, p.String()) + mpreach := p.(*bgp.PathAttributeMpReachNLRI) + if len(mpreach.Value) != 1 { + log.Errorf("include only one route in mp_reach_nlri") + } + nlri = mpreach.Value[0] + ribLocal.NextHop = mpreach.Nexthop + if ribLocal.NextHop.String() == "0.0.0.0" { + ribLocal.IsLocal = true + } + } + case bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI: + if p.(*bgp.PathAttributeMpUnreachNLRI).Value != nil { + log.Debugf("Type Code: [ %d ] MP Unreachable: %v", bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI, p.String()) + } + case bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES: + if p.(*bgp.PathAttributeExtendedCommunities).Value != nil { + log.Debugf("Type Code: [ %d ] Extended Communities: %v", bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES, p.String()) + } + default: + log.Errorf("Unknown BGP attribute code [ %d ]", p.GetType()) + } + } + return ribLocal, nil + +} + +// return string representation of pluginConfig for debugging +func (d *ribLocal) stringer() string { + str := fmt.Sprintf("Prefix:[ %s ], ", d.BgpPrefix.String()) + str = str + fmt.Sprintf("OriginatingIP:[ %s ], ", d.OriginatorIP.String()) + str = str + fmt.Sprintf("Nexthop:[ %s ], ", d.NextHop.String()) + str = str + fmt.Sprintf("IsWithdrawn:[ %t ], ", d.IsWithdraw) + str = str + fmt.Sprintf("IsHostRoute:[ %t ]", d.IsHostRoute) + return str +} + +func parseIPNet(s string) (*net.IPNet, error) { + ip, ipNet, err := net.ParseCIDR(s) + if err != nil { + return nil, err + } + return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil +} diff --git a/drivers/ipvlan/l3_routes.go b/drivers/ipvlan/l3_routes.go new file mode 100644 index 0000000000..292c77ce07 --- /dev/null +++ b/drivers/ipvlan/l3_routes.go @@ -0,0 +1,68 @@ +package ipvlan + +import ( + "fmt" + "net" + + log "github.com/Sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// AddHostRoute adds a route to a device using netlink into the Linux default namespace. +func addNetlinkRoute(neighborNetwork *net.IPNet, nextHop net.IP, netIface string) error { + iface, err := netlink.LinkByName(netIface) + if err != nil { + return err + } + log.Debugf("Adding route learned via BGP for a remote endpoint with:") + log.Debugf("IP Prefix: [ %s ] - Next Hop: [ %s ] - Source Interface: [ %s ]", neighborNetwork, nextHop, iface.Attrs().Name) + return netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: iface.Attrs().Index, + Dst: neighborNetwork, + Gw: nextHop, + }) +} + +// AddHostRoute adds a route to a device using netlink into the Linux default namespace. +func delNetlinkRoute(neighborNetwork *net.IPNet, nextHop net.IP, netIface string) error { + iface, err := netlink.LinkByName(netIface) + if err != nil { + return err + } + log.Debugf("IP Prefix: [ %s ] - Next Hop: [ %s ] - Source Interface: [ %s ]", neighborNetwork, nextHop, iface.Attrs().Name) + return netlink.RouteDel(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: iface.Attrs().Index, + Dst: neighborNetwork, + Gw: nextHop, + }) +} + +// Add a route to the global namespace using the default gateway to determine the iface +func checkAddRoute(dest *net.IPNet, nh net.IP) error { + gwRoutes, err := netlink.RouteGet(nh) + if err != nil { + return fmt.Errorf("route for the next hop %s could not be found: %v", nh, err) + } + return netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: gwRoutes[0].LinkIndex, + Gw: gwRoutes[0].Gw, + Dst: dest, + }) +} + +// AddHostRoute adds a host-scoped route to a device. +func addRoute(neighborNetwork *net.IPNet, nextHop net.IP, iface netlink.Link) error { + log.Debugf("Adding route in the default namespace for IPVlan L3 mode with the following:") + log.Debugf("IP Prefix: [ %s ] - Next Hop: [ %s ] - Source Interface: [ %s ]", + neighborNetwork, nextHop, iface.Attrs().Name) + + return netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: iface.Attrs().Index, + Dst: neighborNetwork, + Gw: nextHop, + }) +}