Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add routes propagation for VRF plugin #874

Merged
merged 1 commit into from Jun 27, 2023

Conversation

korroot
Copy link
Contributor

@korroot korroot commented Mar 31, 2023

Up until now, if previous plugin assigned routes to interface, movement of this interface to new VRF cause routes to be deleted.

This patch adds funtionality to VRF plugin to save the routes before interface is assgined to VRF, and then re-apply all saved routes to new VRF.

Closes: #866

Copy link
Member

@squeed squeed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice!
Can you add a test case that includes both address families?

@korroot
Copy link
Contributor Author

korroot commented Apr 12, 2023

Added IPv6 routing test. All works fine.

@squeed
Copy link
Member

squeed commented Apr 17, 2023

I talked to @mlguerrero12 , who observed that this is a behavior change, and might break existing setups. He's going to run some internal tests on it.

If so, we may need to put this behind a configuration variable, something like preserveRoutes.

Separately, can you fix the lint / format issues?

@korroot
Copy link
Contributor Author

korroot commented Apr 24, 2023

@squeed Lint error fixed.

In terms of behavior change - does it mean that deleting the routing in some cases is desirable?
I am not sure how exactly this configuration variable to enable/disable routes propagation should be implemented?

@korroot korroot force-pushed the vrf-add-routes branch 2 times, most recently from 9f04aab to b654c52 Compare April 24, 2023 19:54
// because otherwise the routes will be deleted after interface is moved.
routes, err := netlink.RouteList(i, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("failed getting ipv4 routes for %s", intf)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not only ipv4

// modify original table to vrf one
// equivalent of 'ip route add <address> table <int>'
route.Table = int(vrf.Table)
netlink.RouteAdd(&route)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unhandled error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I noticed it before, but it is not handled by purpose - the error can be ignored, because if adding a route returns error - it can mean that is already there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in that case, to be consistent with the logic of ipv6 addresses which is above this block, only add the routes that do not exist

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we gain by this? Replacing one-liner with 20-30 lines of code?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then check if the error is because the route is already there. You can't assume that the error will always be due to that. A user might complain that the routes are not being added and we won't have logs to troubleshoot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I have added a check, if route exists, and if not, return error during adding a route.

// modify original table to vrf one
// equivalent of 'ip route add <address> table <int>'
route.Table = int(vrf.Table)
netlink.RouteAdd(&route)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be on the safe side, create a local copy of route inside the for loop

@korroot korroot force-pushed the vrf-add-routes branch 2 times, most recently from 82ebd6d to a3d905a Compare May 15, 2023 13:53
err = netlink.RouteAdd(&r)
if err != nil {
// If route is already present, returned error is "file exists"
if !strings.Contains(fmt.Sprintf("%v", err), "file exists") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does err.Error() not give you the string?

if err != nil {
// If route is already present, returned error is "file exists"
if !strings.Contains(fmt.Sprintf("%v", err), "file exists") {
return fmt.Errorf("error while adding route \"%s\": %v\n", r, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to add \n and perhaps something like "failed adding ..." or "could not add ..." would be more consistent with other errors in this file

// Modify original table to vrf one,
// equivalent of 'ip route add <address> table <int>'.
r.Table = int(vrf.Table)
err = netlink.RouteAdd(&r)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use netlink.RouteReplace instead (ip route replace)

it won't fail if the route exists

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm what do we get by using RouteReplace? I think if the route is there, we maybe not suppose to replace it...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I want to avoid is checking the string of an error to make a decision or even to have an error. I believe we have the means to do it.

When an interface is enslaved, only local and connected routes are moved to the table associated with the vrf. So, instead of fetching all routes (with RouteList), you can use netlink.RouteListFiltered to get all routes of the device that are not connected or local. Something like:

filter := &netlink.Route{
	LinkIndex: link.Attrs().Index, 
	Scope:     netlink.SCOPE_UNIVERSE, // Exclude local and connected routes
}

filterMask := netlink.RT_FILTER_LINK | netlink.RT_FILTER_SCOPE // Filter based on link index and scope

routes, err := netlink.RouteListFiltered(netlink.FAMILY_ALL, filter, filterMask)

and then, add those routes. if we still get an error, then I think we should just return it. The conflict should be investigated and solved by users. Not the task of the plugin to decide what to do (ignore or replace).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense, thanks for the explanation. I will address it shortly.

Up until now, if previous plugin assigned routes to interface, movement of
this interface to new VRF cause routes to be deleted.

This patch adds funtionality to VRF plugin to save the routes before
interface is assgined to VRF, and then re-apply all saved routes to new VRF.

Signed-off-by: Artur Korzeniewski <artur.korzeniewski@travelping.com>
@korroot
Copy link
Contributor Author

korroot commented Jun 2, 2023

@mlguerrero12 your latest comment is addressed, thanks for improving this PR :)
Do you see something more to adjust, or are we closer to merge :D ?

// Modify original table to vrf one,
r.Table = int(vrf.Table)
// equivalent of 'ip route replace <address> table <int>'.
err = netlink.RouteReplace(&r)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use add here now since you are making sure to copy only routes that are not supposed to exist. In case there is a conflict (error), users should be aware of it and fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use the RouteAdd here but it seems that one of IPv6 route is copied to saved routes:

  [FAILED] Unexpected error:
      <*errors.errorString | 0xc0001b56a0>: {
          s: "cmdAdd failed: could not add route '{Ifindex: 2 Dst: abcd:1234:ffff::/64 Src: <nil> Gw: <nil> Flags: [] Table: 1 Realm: 0}': file exists",
      }
      cmdAdd failed: could not add route '{Ifindex: 2 Dst: abcd:1234:ffff::/64 Src: <nil> Gw: <nil> Flags: [] Table: 1 Realm: 0}': file exists
  occurred

From the vrf_test.go: https://github.com/containernetworking/plugins/pull/874/files#diff-d8b21f1b7cbf938511615ea01f7b56215325cb65d64b45b5cd089e01c210bf6eR197

So instead I have used the RouteReplace

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @korroot.

Yes, it seems that the concept of SCOPE_UNIVERSE is different in IPv6 (read about it and ran some tests). There will be conflicts after the ipv6 addresses are added with the block above (or with the sysctl option).

Anyway, I'm fine with using Replace to overcome this. But I think it might be cleaner to check that the Gw is different than nil, and add only those routes (with regular Add). Either option is fine.

@squeed, could you please approve the workflow of this PR?

@korroot
Copy link
Contributor Author

korroot commented Jun 12, 2023

@squeed can you re-review this PR? Also, can somebody re-run the CI? it broke on timeout, not code issues.

@korroot
Copy link
Contributor Author

korroot commented Jun 16, 2023

@squeed thanks for running the CI, can you approve this PR? Or comment if there are any more improvements needed?

@squeed squeed merged commit d03b84d into containernetworking:main Jun 27, 2023
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

VRF plugin is not populating routes
3 participants