Skip to content

Commit e9289b1

Browse files
arghosh93jcaamano
authored andcommitted
Add an unreachable default route for a UDN
This commit is to add a default unreachable route while creating an user defined network: unreachable default metric 4278198272 This PR also allows to mention route priority and type while adding a route to a VRF. Signed-off-by: Arnab Ghosh <[email protected]>
1 parent 1b1f713 commit e9289b1

File tree

6 files changed

+152
-68
lines changed

6 files changed

+152
-68
lines changed

go-controller/pkg/node/controllers/egressip/egressip_test.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/onsi/ginkgo/v2"
2020
"github.com/onsi/gomega"
2121
"github.com/vishvananda/netlink"
22+
"golang.org/x/sys/unix"
2223

2324
corev1 "k8s.io/api/core/v1"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -1133,7 +1134,7 @@ var _ = ginkgo.Describe("VRF", func() {
11331134
ginkgo.By("create VRF and add link")
11341135
gomega.Expect(createVRFAndEnslaveLink(testNS, dummyLink1Name, vrfName, vrfTable)).Should(gomega.Succeed())
11351136
ginkgo.By("add route to routing table associated with VRF")
1136-
vrfRoute := getDstRouteForTable(getLinkIndex(dummyLink1Name), int(vrfTable), dummy3IPv4CIDR)
1137+
vrfRoute := getV4DstRouteForTable(getLinkIndex(dummyLink1Name), int(vrfTable), dummy3IPv4CIDR)
11371138
gomega.Expect(testNS.Do(func(ns.NetNS) error {
11381139
return netlink.RouteAdd(&vrfRoute)
11391140
})).Should(gomega.Succeed())
@@ -1642,31 +1643,49 @@ func isIPTableRuleArgIPV6(ruleArgs []string) bool {
16421643
panic("unable to determine IP version of IPTable rule")
16431644
}
16441645

1646+
// For any IPV4 or IPV6 route, default type is unicast if not mentioned explicitly
1647+
// Ref: https://www.man7.org/linux/man-pages/man8/ip-route.8.html
16451648
func getDefaultIPv4Route(linkIndex int) netlink.Route {
16461649
// dst is nil because netlink represents a default route as nil
1647-
return netlink.Route{LinkIndex: linkIndex, Table: util.CalculateRouteTableID(linkIndex), Dst: defaultV4AnyCIDR}
1650+
return netlink.Route{LinkIndex: linkIndex, Table: util.CalculateRouteTableID(linkIndex), Dst: defaultV4AnyCIDR, Type: unix.RTN_UNICAST}
16481651
}
16491652

16501653
func getDefaultIPv6Route(linkIndex int) netlink.Route {
16511654
// dst is nil because netlink represents a default route as nil
1652-
return netlink.Route{LinkIndex: linkIndex, Table: util.CalculateRouteTableID(linkIndex), Dst: defaultV6AnyCIDR}
1655+
// Priority for a default IPv6 route gets set to 1024
1656+
// Ref : https://access.redhat.com/solutions/3659171
1657+
return netlink.Route{LinkIndex: linkIndex, Table: util.CalculateRouteTableID(linkIndex), Dst: defaultV6AnyCIDR, Type: unix.RTN_UNICAST, Priority: 1024}
16531658
}
16541659

16551660
func getDstRoute(linkIndex int, dst string) netlink.Route {
1656-
return getDstRouteForTable(linkIndex, util.CalculateRouteTableID(linkIndex), dst)
1661+
if utilnet.IsIPv6CIDRString(dst) {
1662+
return getV6DstRouteForTable(linkIndex, util.CalculateRouteTableID(linkIndex), dst)
1663+
} else {
1664+
return getV4DstRouteForTable(linkIndex, util.CalculateRouteTableID(linkIndex), dst)
1665+
}
1666+
1667+
}
16571668

1669+
// Default scope is link for direct unicast routes
1670+
// Ref: https://www.man7.org/linux/man-pages/man8/ip-route.8.html
1671+
func getV4DstRouteForTable(linkIndex, table int, dst string) netlink.Route {
1672+
_, dstIPNet, err := net.ParseCIDR(dst)
1673+
if err != nil {
1674+
panic(err.Error())
1675+
}
1676+
return netlink.Route{LinkIndex: linkIndex, Dst: dstIPNet, Table: table, Type: unix.RTN_UNICAST, Scope: netlink.SCOPE_LINK}
16581677
}
16591678

1660-
func getDstRouteForTable(linkIndex, table int, dst string) netlink.Route {
1679+
func getV6DstRouteForTable(linkIndex, table int, dst string) netlink.Route {
16611680
_, dstIPNet, err := net.ParseCIDR(dst)
16621681
if err != nil {
16631682
panic(err.Error())
16641683
}
1665-
return netlink.Route{LinkIndex: linkIndex, Dst: dstIPNet, Table: table}
1684+
return netlink.Route{LinkIndex: linkIndex, Dst: dstIPNet, Table: table, Type: unix.RTN_UNICAST, Priority: 256}
16661685
}
16671686

16681687
func getLinkLocalRoute(linkIndex int) netlink.Route {
1669-
return netlink.Route{LinkIndex: linkIndex, Dst: linkLocalCIDR, Table: util.CalculateRouteTableID(linkIndex)}
1688+
return netlink.Route{LinkIndex: linkIndex, Dst: linkLocalCIDR, Table: util.CalculateRouteTableID(linkIndex), Type: unix.RTN_UNICAST, Priority: 256}
16701689
}
16711690

16721691
func getNetlinkAddr(ip, netmask string) *netlink.Addr {

go-controller/pkg/node/gateway_udn.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/vishvananda/netlink"
12+
"golang.org/x/sys/unix"
1213

1314
corev1 "k8s.io/api/core/v1"
1415
"k8s.io/apimachinery/pkg/util/wait"
@@ -670,7 +671,31 @@ func (udng *UserDefinedNetworkGateway) computeRoutesForUDN(mpLink netlink.Link)
670671
}
671672
}
672673
}
673-
674+
// Add unreachable route to enure that kernel always finds a match to the VRF table rather than
675+
// referring to default VRF table and send traffic via unwanted interfaces and to unwanted gateway.
676+
// non 0 link index for an unreachable or blackhole IPv4 route returns 'invalid argument'
677+
hasV4Subnet, hasV6Subnet := udng.IPMode()
678+
if hasV4Subnet {
679+
_, v4AnyCIDR, _ := net.ParseCIDR("0.0.0.0/0")
680+
retVal = append(retVal, netlink.Route{
681+
Dst: v4AnyCIDR,
682+
Table: udng.vrfTableId,
683+
Priority: 4278198272,
684+
Type: unix.RTN_UNREACHABLE,
685+
})
686+
}
687+
// link index for an unreachable IPv6 route always get set to 1. Link index 1 refers to default loopback
688+
// device at all time. Reference: https://docs.kernel.org/networking/vrf.html#using-iproute2-for-vrfs
689+
if hasV6Subnet {
690+
_, v6AnyCIDR, _ := net.ParseCIDR("::/0")
691+
retVal = append(retVal, netlink.Route{
692+
LinkIndex: types.LoopbackInterfaceIndex,
693+
Dst: v6AnyCIDR,
694+
Table: udng.vrfTableId,
695+
Priority: 4278198272,
696+
Type: unix.RTN_UNREACHABLE,
697+
})
698+
}
674699
return retVal, nil
675700
}
676701

go-controller/pkg/node/gateway_udn_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
nadfake "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned/fake"
1515
"github.com/stretchr/testify/mock"
1616
"github.com/vishvananda/netlink"
17+
"golang.org/x/sys/unix"
1718

1819
corev1 "k8s.io/api/core/v1"
1920
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -1051,7 +1052,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
10511052

10521053
routes, err := udnGateway.computeRoutesForUDN(mplink)
10531054
Expect(err).NotTo(HaveOccurred())
1054-
Expect(routes).To(HaveLen(7))
1055+
Expect(routes).To(HaveLen(9))
10551056
Expect(err).NotTo(HaveOccurred())
10561057
Expect(*routes[0].Dst).To(Equal(*ovntest.MustParseIPNet("172.16.1.0/24"))) // default service subnet
10571058
Expect(routes[0].LinkIndex).To(Equal(bridgelink.Attrs().Index))
@@ -1089,7 +1090,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
10891090
Expect(err).NotTo(HaveOccurred())
10901091
Expect(fexec.CalledMatchesExpected()).To(BeTrue(), fexec.ErrorDesc)
10911092
})
1092-
ovntest.OnSupportedPlatformsIt("should compute correct service routes for a user defined network", func() {
1093+
ovntest.OnSupportedPlatformsIt("should compute correct routes for a user defined network", func() {
10931094
config.Gateway.Interface = "eth0"
10941095
config.IPv4Mode = true
10951096
config.IPv6Mode = true
@@ -1122,7 +1123,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
11221123

11231124
routes, err := udnGateway.computeRoutesForUDN(mplink)
11241125
Expect(err).NotTo(HaveOccurred())
1125-
Expect(routes).To(HaveLen(8))
1126+
Expect(routes).To(HaveLen(10))
11261127
Expect(err).NotTo(HaveOccurred())
11271128
// 1st and 2nd routes are the service routes from user-provided config value
11281129
Expect(*routes[0].Dst).To(Equal(*config.Kubernetes.ServiceCIDRs[0]))
@@ -1157,6 +1158,16 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
11571158
Expect(*routes[7].Dst).To(Equal(*ovntest.MustParseIPNet("ae70::/60"))) // cluster subnet route
11581159
Expect(routes[7].LinkIndex).To(Equal(mplink.Attrs().Index))
11591160
Expect(routes[7].Gw.Equal(ovntest.MustParseIP("ae70::1"))).To(BeTrue())
1161+
1162+
// IPv4 default unreachable route
1163+
Expect(*routes[8].Dst).To(Equal(*ovntest.MustParseIPNet("0.0.0.0/0"))) // cluster subnet route
1164+
Expect(routes[8].Priority).To(Equal(4278198272))
1165+
Expect(routes[8].Type).To(Equal(unix.RTN_UNREACHABLE))
1166+
1167+
// IPv6 default unreachable route
1168+
Expect(*routes[9].Dst).To(Equal(*ovntest.MustParseIPNet("::/0"))) // cluster subnet route
1169+
Expect(routes[9].Priority).To(Equal(4278198272))
1170+
Expect(routes[9].Type).To(Equal(unix.RTN_UNREACHABLE))
11601171
return nil
11611172
})
11621173
Expect(err).NotTo(HaveOccurred())

go-controller/pkg/node/routemanager/route_manager.go

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,13 @@ func (c *Controller) addRoute(r netlink.Route) error {
101101
// already managed - nothing to do
102102
return nil
103103
}
104-
link, err := util.GetNetLinkOps().LinkByIndex(r.LinkIndex)
105-
if err != nil {
106-
return fmt.Errorf("failed to apply route (%s) because unable to get link: %v", r.String(), err)
104+
if r.LinkIndex != 0 {
105+
_, err := util.GetNetLinkOps().LinkByIndex(r.LinkIndex)
106+
if err != nil {
107+
return fmt.Errorf("failed to apply route (%s) because unable to get link: %v", r.String(), err)
108+
}
107109
}
108-
if err := c.applyRoute(link, r.Gw, r.Dst, r.MTU, r.Src, r.Table); err != nil {
110+
if err := c.applyRoute(r.LinkIndex, r.Gw, r.Dst, r.MTU, r.Src, r.Table, r.Priority, r.Type, r.Scope); err != nil {
109111
return fmt.Errorf("failed to apply route (%s): %v", r.String(), err)
110112
}
111113
klog.Infof("Route Manager: completed adding route: %s", r.String())
@@ -118,15 +120,17 @@ func (c *Controller) delRoute(r netlink.Route) error {
118120
c.Lock()
119121
defer c.Unlock()
120122
klog.Infof("Route Manager: attempting to delete route: %s", r.String())
121-
link, err := util.GetNetLinkOps().LinkByIndex(r.LinkIndex)
122-
if err != nil {
123-
if util.GetNetLinkOps().IsLinkNotFoundError(err) {
124-
delete(c.store, r.LinkIndex)
125-
return nil
123+
if r.LinkIndex != 0 {
124+
_, err := util.GetNetLinkOps().LinkByIndex(r.LinkIndex)
125+
if err != nil {
126+
if util.GetNetLinkOps().IsLinkNotFoundError(err) {
127+
delete(c.store, r.LinkIndex)
128+
return nil
129+
}
130+
return fmt.Errorf("failed to delete route (%s) because unable to get link: %v", r.String(), err)
126131
}
127-
return fmt.Errorf("failed to delete route (%s) because unable to get link: %v", r.String(), err)
128132
}
129-
if err := c.netlinkDelRoute(link, r.Dst, r.Table); err != nil {
133+
if err := c.netlinkDelRoute(r.LinkIndex, r.Dst, r.Table); err != nil {
130134
return fmt.Errorf("failed to delete route (%s): %v", r.String(), err)
131135
}
132136
managedRoutes, ok := c.store[r.LinkIndex]
@@ -171,27 +175,31 @@ func (c *Controller) processNetlinkEvent(ru netlink.RouteUpdate) error {
171175
}
172176
for _, managedRoute := range managedRoutes {
173177
if RoutePartiallyEqual(managedRoute, ru.Route) {
174-
link, err := util.GetNetLinkOps().LinkByIndex(managedRoute.LinkIndex)
175-
if err != nil {
176-
klog.Errorf("Route Manager: failed to restore route because unable to get link by index %d: %v", managedRoute.LinkIndex, err)
177-
continue
178+
if managedRoute.LinkIndex != 0 {
179+
_, err := util.GetNetLinkOps().LinkByIndex(managedRoute.LinkIndex)
180+
if err != nil {
181+
klog.Errorf("Route Manager: failed to restore route because unable to get link by index %d: %v", managedRoute.LinkIndex, err)
182+
continue
183+
}
178184
}
179-
if err = c.applyRoute(link, managedRoute.Gw, managedRoute.Dst, managedRoute.MTU, managedRoute.Src, managedRoute.Table); err != nil {
185+
if err := c.applyRoute(managedRoute.LinkIndex, managedRoute.Gw, managedRoute.Dst, managedRoute.MTU, managedRoute.Src, managedRoute.Table,
186+
managedRoute.Priority, managedRoute.Type, managedRoute.Scope); err != nil {
180187
klog.Errorf("Route Manager: failed to apply route (%s): %v", managedRoute.String(), err)
181188
}
182189
}
183190
}
184191
return nil
185192
}
186193

187-
func (c *Controller) applyRoute(link netlink.Link, gwIP net.IP, subnet *net.IPNet, mtu int, src net.IP, table int) error {
188-
filterRoute, filterMask := filterRouteByDstAndTable(link.Attrs().Index, subnet, table)
194+
func (c *Controller) applyRoute(linkIndex int, gwIP net.IP, subnet *net.IPNet, mtu int, src net.IP,
195+
table, priority, rtype int, scope netlink.Scope) error {
196+
filterRoute, filterMask := filterRouteByDstAndTable(linkIndex, subnet, table)
189197
existingRoutes, err := util.GetNetLinkOps().RouteListFiltered(getNetlinkIPFamily(subnet), filterRoute, filterMask)
190198
if err != nil {
191199
return fmt.Errorf("failed to list filtered routes: %v", err)
192200
}
193201
if len(existingRoutes) == 0 {
194-
return c.netlinkAddRoute(link, gwIP, subnet, mtu, src, table)
202+
return c.netlinkAddRoute(linkIndex, gwIP, subnet, mtu, src, table, priority, rtype, scope)
195203
}
196204
netlinkRoute := &existingRoutes[0]
197205
if netlinkRoute.MTU != mtu || !src.Equal(netlinkRoute.Src) || !gwIP.Equal(netlinkRoute.Gw) {
@@ -207,10 +215,11 @@ func (c *Controller) applyRoute(link netlink.Link, gwIP net.IP, subnet *net.IPNe
207215
return nil
208216
}
209217

210-
func (c *Controller) netlinkAddRoute(link netlink.Link, gwIP net.IP, subnet *net.IPNet, mtu int, srcIP net.IP, table int) error {
218+
func (c *Controller) netlinkAddRoute(linkIndex int, gwIP net.IP, subnet *net.IPNet,
219+
mtu int, srcIP net.IP, table, priority, rtype int, scope netlink.Scope) error {
211220
newNlRoute := &netlink.Route{
212221
Dst: subnet,
213-
LinkIndex: link.Attrs().Index,
222+
LinkIndex: linkIndex,
214223
Scope: netlink.SCOPE_UNIVERSE,
215224
Table: table,
216225
}
@@ -223,21 +232,31 @@ func (c *Controller) netlinkAddRoute(link netlink.Link, gwIP net.IP, subnet *net
223232
if mtu != 0 {
224233
newNlRoute.MTU = mtu
225234
}
235+
if priority != 0 {
236+
newNlRoute.Priority = priority
237+
}
238+
if rtype != 0 {
239+
newNlRoute.Type = rtype
240+
}
241+
if scope != netlink.Scope(0) {
242+
newNlRoute.Scope = scope
243+
}
226244
err := util.GetNetLinkOps().RouteAdd(newNlRoute)
227245
if err != nil {
228-
return fmt.Errorf("failed to add route (gw: %v, subnet %v, mtu %d, src IP %v): %v", gwIP, subnet, mtu, srcIP, err)
246+
return fmt.Errorf("failed to add route (linkIndex: %d gw: %v, subnet %v, mtu %d, src IP %v): %v",
247+
newNlRoute.LinkIndex, gwIP, subnet, mtu, srcIP, err)
229248
}
230249
return nil
231250
}
232251

233-
func (c *Controller) netlinkDelRoute(link netlink.Link, subnet *net.IPNet, table int) error {
252+
func (c *Controller) netlinkDelRoute(linkIndex int, subnet *net.IPNet, table int) error {
234253
if subnet == nil {
235254
return fmt.Errorf("cannot delete route with no valid subnet")
236255
}
237-
filter, mask := filterRouteByDstAndTable(link.Attrs().Index, subnet, table)
256+
filter, mask := filterRouteByDstAndTable(linkIndex, subnet, table)
238257
existingRoutes, err := util.GetNetLinkOps().RouteListFiltered(netlink.FAMILY_ALL, filter, mask)
239258
if err != nil {
240-
return fmt.Errorf("failed to get routes for link %s: %v", link.Attrs().Name, err)
259+
return fmt.Errorf("failed to get routes for link %d: %v", linkIndex, err)
241260
}
242261
for _, existingRoute := range existingRoutes {
243262
if err = util.GetNetLinkOps().RouteDel(&existingRoute); err != nil {
@@ -286,16 +305,19 @@ func (c *Controller) sync() {
286305
}
287306
}
288307
if !found {
289-
link, err := util.GetNetLinkOps().LinkByIndex(managedRoute.LinkIndex)
290-
if err != nil {
291-
if util.GetNetLinkOps().IsLinkNotFoundError(err) {
292-
deletedLinkIndexes = append(deletedLinkIndexes, linkIndex)
293-
} else {
294-
klog.Errorf("Route Manager: failed to apply route (%s) because unable to retrieve associated link: %v", managedRoute.String(), err)
308+
if managedRoute.LinkIndex != 0 {
309+
_, err := util.GetNetLinkOps().LinkByIndex(managedRoute.LinkIndex)
310+
if err != nil {
311+
if util.GetNetLinkOps().IsLinkNotFoundError(err) {
312+
deletedLinkIndexes = append(deletedLinkIndexes, linkIndex)
313+
} else {
314+
klog.Errorf("Route Manager: failed to apply route (%s) because unable to retrieve associated link: %v", managedRoute.String(), err)
315+
}
316+
continue
295317
}
296-
continue
297318
}
298-
if err := c.applyRoute(link, managedRoute.Gw, managedRoute.Dst, managedRoute.MTU, managedRoute.Src, managedRoute.Table); err != nil {
319+
if err := c.applyRoute(managedRoute.LinkIndex, managedRoute.Gw, managedRoute.Dst, managedRoute.MTU, managedRoute.Src, managedRoute.Table,
320+
managedRoute.Priority, managedRoute.Type, managedRoute.Scope); err != nil {
299321
klog.Errorf("Route Manager: failed to apply route (%s): %v", managedRoute.String(), err)
300322
}
301323
}
@@ -353,5 +375,8 @@ func RoutePartiallyEqual(r, x netlink.Route) bool {
353375
r.Gw.Equal(x.Gw) &&
354376
r.Table == x.Table &&
355377
r.Flags == x.Flags &&
356-
r.MTU == x.MTU
378+
r.MTU == x.MTU &&
379+
r.Type == x.Type &&
380+
r.Priority == x.Priority &&
381+
r.Scope == x.Scope
357382
}

0 commit comments

Comments
 (0)