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 Pseudowire management in Zebra #601

Closed
wants to merge 3 commits into from

Conversation

bingen
Copy link
Member

@bingen bingen commented May 22, 2017

When Zebra receives a PW from ldpd, it tries to install it in kernel and
in case of failure notifies back to ldpd daemon and enqueues the PW for
retry.

The installation to kernel is not yet implemented as Linux kernel
doesn't have support for it yet. It will be done through a module hook,
so it can be outsourced to a remote dataplane.

Signed-off-by: ßingen bingen@voltanet.io

When Zebra receives a PW from ldpd, it tries to install it in kernel and
in case of failure notifies back to ldpd daemon and enqueues the PW for
retry.

The installation to kernel is not yet implemented as Linux kernel
doesn't have support for it yet. It will be done through a module hook,
so it can be outsourced to a remote dataplane.

Signed-off-by: ßingen <bingen@voltanet.io>
@NetDEF-CI
Copy link
Collaborator

Continous Integration Result: SUCCESSFUL

Congratulations, this patch passed basic tests

Tested-by: NetDEF / OpenSourceRouting.org CI System

CI System Testrun URL: https://ci1.netdef.org/browse/FRR-FRRPULLREQ-786/

This is a comment from an EXPERIMENTAL automated CI system.
For questions and feedback in regards to this CI system, please feel free to email
Martin Winter - mwinter (at) opensourcerouting.org.


CLANG Static Analyzer Summary

  • Github Pull Request 601, comparing to Git base SHA 74572f0

No Changes in Static Analysis warnings compared to base

132 Static Analyzer issues remaining.

See details at
https://ci1.netdef.org/browse/FRR-FRRPULLREQ-786/artifact/shared/static_analysis/index.html

@donaldsharp
Copy link
Member

This looks isolated and after a review from @rwestphal it can be integrated

@NetDEF-CI
Copy link
Collaborator

Continous Integration Result: SUCCESSFUL

Congratulations, this patch passed basic tests

Tested-by: NetDEF / OpenSourceRouting.org CI System

CI System Testrun URL: https://ci1.netdef.org/browse/FRR-FRRPULLREQ-821/

This is a comment from an EXPERIMENTAL automated CI system.
For questions and feedback in regards to this CI system, please feel free to email
Martin Winter - mwinter (at) opensourcerouting.org.


CLANG Static Analyzer Summary

  • Github Pull Request 601, comparing to Git base SHA 74572f0

No Changes in Static Analysis warnings compared to base

132 Static Analyzer issues remaining.

See details at
https://ci1.netdef.org/browse/FRR-FRRPULLREQ-821/artifact/shared/static_analysis/index.html

@rwestphal
Copy link
Member

@bingen please push this against master and not stable/3.0, as it is a new feature. More feedback soon.

Copy link
Member

@rwestphal rwestphal left a comment

Choose a reason for hiding this comment

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

Good work, this is the missing piece of VPLS support in FRR.

Please address my nits and re-open this against master. In the meantime I'll cook a patch on top of this to install the LDP signaled pseudowires in the kernel when running on OpenBSD, mostly as a proof of concept.

@@ -188,6 +190,7 @@ void fec_snap(struct lde_nbr *);
void fec_tree_clear(void);
struct fec_nh *fec_nh_find(struct fec_node *, int, union ldpd_addr *,
ifindex_t, uint8_t);
void update_local_label (struct fec_node *fn, int connected);
Copy link
Member

Choose a reason for hiding this comment

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

unused prototype

int
kmpw_set(struct kpw *kpw)
{
zebra_send_kpw (ZEBRA_KPW_ADD, zclient, kpw);
Copy link
Member

Choose a reason for hiding this comment

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

Please remove the TODO comment below.

Better yet, rewrite this function like this:
return (zebra_send_kpw (ZEBRA_PW_ADD, zclient, kpw));

return (0);
}

int
kmpw_unset(struct kpw *kpw)
{
zebra_send_kpw (ZEBRA_KPW_DELETE, zclient, kpw);
Copy link
Member

Choose a reason for hiding this comment

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

Same here

@@ -25,6 +25,8 @@
#include "openbsd-tree.h"
#include "if.h"

#define RETRY_SYNC_PW_INTERVAL 5 /* in seconds */
Copy link
Member

Choose a reason for hiding this comment

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

This is not used anywhere

@@ -94,6 +94,9 @@ typedef enum {
ZEBRA_LABEL_MANAGER_CONNECT,
ZEBRA_GET_LABEL_CHUNK,
ZEBRA_RELEASE_LABEL_CHUNK,
ZEBRA_KPW_ADD,
Copy link
Member

Choose a reason for hiding this comment

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

Forgive the bikeshedding, but I'd prefer to remove the "K" from these constants.

ZEBRA_PW_ADD and ZEBRA_PW_DELETE look better.

int af;
union ldpd_addr nexthop;
uint32_t local_label;
uint32_t remote_label;
uint8_t flags;
uint32_t pwid;
char vpn_name[L2VPN_NAME_LEN];
unsigned short ac_port_ifindex;
};
Copy link
Member

Choose a reason for hiding this comment

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

What is the ac_port_ifindex for? It's not being used anywhere.

Also, the other members you're adding here are not necessary in my opinion.

We don't want to send protocol-specific pseudowire attributes to zebra, like pwid, lsr_id and vpn_name. Currently only ldpd supports L2VPNs, but in the future we should implement support for BGP-signaled L2VPNs as well.

I think that pseudowires can be uniquely identified by their ifname and ifindex, zebra doesn't need to track more information than that.

{
struct stream *s;

debug_zebra_out("ILM %s PW %u (%s) ifindex %hu, type %d. %s -> nexthop %s label %s",
Copy link
Member

Choose a reason for hiding this comment

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

ILM?

zebra_size_t length, vrf_id_t vrf_id)
{
struct stream *s;
uint8_t status;
Copy link
Member

Choose a reason for hiding this comment

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

padding not consistent with the rest of the code

@bingen
Copy link
Member Author

bingen commented May 31, 2017

@rwestphal thanks for your feedback, I will address it soon but a first comment about where to submit the PR to. This is supposed to be related to commit 595b4be which is in stable/3.0, so I think it should be there. As we talked by e-mail some time ago, with the code in stable/3.0 as it is now, it will show the PW status as UP by default, even if it's not true.

@rwestphal
Copy link
Member

@bingen In my opinion this PR is way too big for stable/3.0.

If you really need the pseudowire manager on stable/3.0 you can always create an internal fork to accommodate your custom requirements.

The commit you pointed out didn't break anything because VPLS is not supposed to work without a pseudowire manager in zebra. On stable/3.0, all pseudowires signaled by ldpd are going to /dev/null. We could revert 595b4be, but would that make any difference?

If the label is implicit null it's not installed in Zebra, so this check
doesn't make sense.

Signed-off-by: ßingen <bingen@voltanet.io>
@NetDEF-CI
Copy link
Collaborator

Continous Integration Result: SUCCESSFUL

Congratulations, this patch passed basic tests

Tested-by: NetDEF / OpenSourceRouting.org CI System

CI System Testrun URL: https://ci1.netdef.org/browse/FRR-FRRPULLREQ-839/

This is a comment from an EXPERIMENTAL automated CI system.
For questions and feedback in regards to this CI system, please feel free to email
Martin Winter - mwinter (at) opensourcerouting.org.


CLANG Static Analyzer Summary

  • Github Pull Request 601, comparing to Git base SHA 74572f0

No Changes in Static Analysis warnings compared to base

132 Static Analyzer issues remaining.

See details at
https://ci1.netdef.org/browse/FRR-FRRPULLREQ-839/artifact/shared/static_analysis/index.html

zebra/zebra_pw.c Outdated
zlog_warn ("No label found in rib for PW %u at VPN %s", pw->pwid,
pw->vpn_name);
return 1;
}
Copy link
Member

Choose a reason for hiding this comment

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

If the label is implicit null it's not installed in Zebra, so this check doesn't make sense.

Not really, you can't remove this.

The P from VPLS stands for Private. Removing these lines introduces a security issue as you break the isolation between the VPLS customers.

Receiving an implicit-null label for the pseudowire's remote endpoint is different than receiving no label at all.

If you receive an implicit-null label, it means that the remote endpoint is directly connected to you. In this case, due to the default PHP behavior, you can send the pseudowire frames with the VPN label only. You don't need to push an additional label to tunnel the packets to the remote endpoint.

Now let's say that you didn't receive any label for the pseudowire's remote endpoint. In this case, you don't know if the remote endpoint is directly connected to you or not. If you send L2VPN frames pushing just one label (the VPN label) and the nexthop is not the remote endpoint, then it will not understand what to do with the VPN label. Even worse, it might have allocated the very same label for another VPLS client and forward the packet to it. Now you have a security issue. This is possible because labels are locally allocated in standard MPLS networks (when segment routing is not in use).

With that said, the real problem here is that we're not adding LDP's implicit-null labels to routes. Removing the out_label != MPLS_IMP_NULL_LABEL checks below (zserv.c) should solve the problem.

  if (command == ZEBRA_MPLS_LABELS_ADD)
    {
      mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate,
                        ifindex);
      if (out_label != MPLS_IMP_NULL_LABEL)
        mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex,
                         distance, out_label);
    }
  else if (command == ZEBRA_MPLS_LABELS_DELETE)
    {
      mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, ifindex);
      if (out_label != MPLS_IMP_NULL_LABEL)
        mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex,
                         distance, out_label);
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

Ya, you are right, good catch. I saw the other code (where it discards Implicit Null labels) but I didn't touch because I thought that it would be there for a reason and I was afraid of breaking something else.
I'll try removing it and restoring the check. If it works I'll add it to my final PR when I address all of your comments. Are you sure it's safe to install those implicit null labels? Why was that code put there then?
Thanks.

Copy link
Member

Choose a reason for hiding this comment

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

It should be safe.

Back when I wrote this code I thought we could avoid making unnecessary changes in the RIB because implicit-null labels are never sent on the wire. But it turns out that we need to keep track of implicit-null labels in the RIB because of this issue you brought up. rt_netlink.c and zebra_mpls_openbsd.c have additional checks that will ensure that the implicit-null labels are never installed in the kernel.

@bingen
Copy link
Member Author

bingen commented Jun 1, 2017

@rwestphal About the branching I completely disagree. This PR is addressing something that was removed in a previous commit, as you said here "we assume that zebra will do that for us". So without this PR stable/3.0 is with no doubt broken. You say nobody uses it. Well, you already know that we do, but if that's true, why would it matter then to accept this PR here? I see very inconsistent to accept in stable branch the removal of a check but not its substitute. It makes no sense to me.
About creating an internal fork, ya, of course we can do that, but then we'll have to be manually merging all the bugfixes that come up to stable 3.0, so not very helpful.
Finally, looking at here, I wouldn't say that this is new to master precisely because of what I said (I'm just restoring something previously removed in stable/3.0), so I don't understand how you are applying these criteria here.
But anyway, these are my final thoughts. As you have to accept the PR, if you still disagree with me I'll submit my new PR against master.

@rwestphal
Copy link
Member

My point is that with or without that commit VPLS doesn't work on stable/3.0. That commit just prepares the ground for the pseudowire manager, reverting it for example wouldn't change anything.

For me it's questionable if your PR is adding a new feature or fixing a bug, it's kinda doing both. Generally speaking I'd prefer to avoid big changes on a stable branch. But since the code is well contained I'll speak with the other maintainers and see if we can add an exception here.

@donaldsharp
Copy link
Member

some more issues that need to be worked on a bit more.

@rwestphal
Copy link
Member

As we talked in Slack before, WQ_RETRY_LATER doesn't work the way one would expect. After a failure, a new attempt to install the pseudowire is made right away, without waiting for the workqueue's holdtime (zebra->pwq->spec.hold). Since zebra->pwq->spec.max_retries is set to 3 by default, if something goes wrong then 4 attempts to install the pseudowire are made in a row and then we just give up.

In my VPLS test setup things are not going quite right because of this problem.

One other problem is that nexthop resolution is being performed only when the pseudowire is installed. If the route and/or label used to reach the pseudowire's remote endpoint changes, we need to reinstall the pseudowire in the kernel/hardware.

With that said, we have two possibilities here:
1 - Use a poll-based approach: every N seconds loop through all available pseudowires and check the ones that need to be installed/updated/removed;
2 - Use an event-based approach: do the same thing every time the RIB is updated (e.g. in the end of meta_queue_process_complete()).

Approach #2 is more responsive but potentially more expensive. One other problem with #2 is that the installation of a pseudowire can fail for other reasons other than failed nexthop resolution (e.g. hardware running out of resources). In this case, we can wait too long to re-attempt to install a pseudowire if the RIB is not being changed. Maybe implementing a hybrid of #1 and #2 would be the way to go, so we're good in all cases. Does anyone have an opinion on this?

@rwestphal
Copy link
Member

Update: I talked to @eqvinox yesterday and he suggested that using our NHT code might be appropriate to solve the pseudowire nexthop resolution problem. I'm investigating this idea right now.

@rwestphal
Copy link
Member

Implemented here nexthop tracking in ldpd.

Now ldpd registers the nexthop of the pseudowires with zebra and keeps track if they are resolvable or not. When the nexthop resolution for a pseudowire changes, ldpd will send a message to zebra asking to reinstall or uninstall the pseudowire.

The only issue here is that ldpd needs labeled routes for its pseudowires remote endpoints (bgpd should have the same requirement for MPLS VPN routes). In other words, a resolved nexthop is only valid if it's labeled, otherwise the pseudowire can't be installed.

To address this issue I did some small changes in the ZEBRA_NEXTHOP_UPDATE message to include the label of the nexthops as well. So far so good, but there's a problem. When we add a label to a route, zebra doesn't notify its NHT clients that this route was updated.

Apparently the problem comes down to this line: https://github.com/FRRouting/frr/blob/ce7fce36b/zebra/zebra_rnh.c#L870

We do set ROUTE_ENTRY_NEXTHOPS_CHANGED when we add a label to a route, but this flag is always unset before we reach this compare_state() function.

More precisely, the NHT call trace is usually like this:

meta_queue_process_complete()
  zebra_evaluate_rnh()
    zebra_rnh_evaluate_entry()
      zebra_rnh_eval_nexthop_entry()
        compare_state()
          CHECK_FLAG(r1->status, ROUTE_ENTRY_NEXTHOPS_CHANGED)
        zebra_rnh_notify_protocol_clients()
          send_client()

meta_queue_process_complete() is the completion_func callback of the RIB workqueue. When it's called the ROUTE_ENTRY_NEXTHOPS_CHANGED flag has been cleared from all routes already (https://github.com/FRRouting/frr/blob/ce7fce36b/zebra/zebra_rib.c#L1561).

With that said, I propose to remove the aforementioned line (zebra_rib.c#L1561) in order to solve this problem. Removing this line will allow the ROUTE_ENTRY_NEXTHOPS_CHANGED flag to propagate until zebra_evaluate_rnh(), allowing it to detect changed nexthops. This flag is then unset in zebra_rnh_clear_nhc_flag() for all routes (https://github.com/FRRouting/frr/blob/ce7fce36b/zebra/zebra_rnh.c#L719).

Any thoughts? I'm not super sure if doing this is safe or not so I'd appreciate feedback from others.

@donaldsharp
Copy link
Member

To close the loop from a private Conversation @rwestphal and I had last night. I do not believe modifying zebra_rnh.c is the right thing to do. Currently when labels change for a route entry we are not considering that a change in the nexthop so we are not calling the rnh code correctly. We should be modifying rib_process( and really nexthop_active_update/nexthop_active..) to be able to notice when labels are changed in the nexthop

@bingen
Copy link
Member Author

bingen commented Jun 13, 2017

Replaced by #710

@bingen bingen closed this Jun 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants