diff options
author | Donald Sharp <sharpd@cumulusnetworks.com> | 2017-08-08 13:54:32 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-08 13:54:32 +0200 |
commit | f66e92bc4856352dc4c1c81fa35b1dd570cd83e5 (patch) | |
tree | d95a4b505cc238c09ea4d2512f2ad6af92f5cb9c | |
parent | Merge pull request #907 from opensourcerouting/vty-close-3.0 (diff) | |
parent | zebra: add support for static pseudowires (diff) | |
download | frr-f66e92bc4856352dc4c1c81fa35b1dd570cd83e5.tar.xz frr-f66e92bc4856352dc4c1c81fa35b1dd570cd83e5.zip |
Merge pull request #783 from opensourcerouting/pw-manager-2
Add Pseudowire management in Zebra
-rw-r--r-- | ldpd/l2vpn.c | 65 | ||||
-rw-r--r-- | ldpd/lde.c | 60 | ||||
-rw-r--r-- | ldpd/lde.h | 1 | ||||
-rw-r--r-- | ldpd/lde_lib.c | 9 | ||||
-rw-r--r-- | ldpd/ldp.h | 3 | ||||
-rw-r--r-- | ldpd/ldp_zebra.c | 76 | ||||
-rw-r--r-- | ldpd/ldpd.c | 41 | ||||
-rw-r--r-- | ldpd/ldpd.h | 33 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/command.c | 2 | ||||
-rw-r--r-- | lib/command.h | 1 | ||||
-rw-r--r-- | lib/log.c | 5 | ||||
-rw-r--r-- | lib/pw.h | 53 | ||||
-rw-r--r-- | lib/vty.c | 2 | ||||
-rw-r--r-- | lib/zclient.c | 70 | ||||
-rw-r--r-- | lib/zclient.h | 38 | ||||
-rw-r--r-- | vtysh/Makefile.am | 1 | ||||
-rw-r--r-- | vtysh/vtysh.c | 25 | ||||
-rw-r--r-- | vtysh/vtysh_config.c | 2 | ||||
-rw-r--r-- | zebra/Makefile.am | 7 | ||||
-rw-r--r-- | zebra/debug.c | 26 | ||||
-rw-r--r-- | zebra/debug.h | 4 | ||||
-rw-r--r-- | zebra/main.c | 1 | ||||
-rw-r--r-- | zebra/rib.h | 1 | ||||
-rw-r--r-- | zebra/zebra_mpls.c | 4 | ||||
-rw-r--r-- | zebra/zebra_mpls_openbsd.c | 100 | ||||
-rw-r--r-- | zebra/zebra_pw.c | 532 | ||||
-rw-r--r-- | zebra/zebra_pw.h | 75 | ||||
-rw-r--r-- | zebra/zebra_pw_null.c | 29 | ||||
-rw-r--r-- | zebra/zebra_rnh.c | 89 | ||||
-rw-r--r-- | zebra/zebra_rnh.h | 3 | ||||
-rw-r--r-- | zebra/zebra_vrf.c | 2 | ||||
-rw-r--r-- | zebra/zebra_vrf.h | 5 | ||||
-rw-r--r-- | zebra/zebra_vty.c | 7 | ||||
-rw-r--r-- | zebra/zserv.c | 138 | ||||
-rw-r--r-- | zebra/zserv.h | 3 |
36 files changed, 1419 insertions, 95 deletions
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 27948f5a1..afb9528d8 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -234,6 +234,7 @@ void l2vpn_pw_init(struct l2vpn_pw *pw) { struct fec fec; + struct zapi_pw zpw; l2vpn_pw_reset(pw); @@ -241,16 +242,23 @@ l2vpn_pw_init(struct l2vpn_pw *pw) lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0, 0, (void *)pw); lde_kernel_update(&fec); + + pw2zpw(pw, &zpw); + lde_imsg_compose_parent(IMSG_KPW_ADD, 0, &zpw, sizeof(zpw)); } void l2vpn_pw_exit(struct l2vpn_pw *pw) { struct fec fec; + struct zapi_pw zpw; l2vpn_pw_fec(pw, &fec); lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0); lde_kernel_update(&fec); + + pw2zpw(pw, &zpw); + lde_imsg_compose_parent(IMSG_KPW_DELETE, 0, &zpw, sizeof(zpw)); } static void @@ -268,7 +276,8 @@ l2vpn_pw_reset(struct l2vpn_pw *pw) { pw->remote_group = 0; pw->remote_mtu = 0; - pw->remote_status = 0; + pw->local_status = PW_FORWARDING; + pw->remote_status = PW_NOT_FORWARDING; if (pw->flags & F_PW_CWORD_CONF) pw->flags |= F_PW_CWORD; @@ -474,6 +483,56 @@ l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm) } } +int +l2vpn_pw_status_update(struct zapi_pw_status *zpw) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw = NULL; + struct lde_nbr *ln; + struct fec fec; + uint32_t local_status; + + RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) { + pw = l2vpn_pw_find(l2vpn, zpw->ifname); + if (pw) + break; + } + if (!pw) { + log_warnx("%s: pseudowire %s not found", __func__, zpw->ifname); + return (1); + } + + if (zpw->status == PW_STATUS_UP) + local_status = PW_FORWARDING; + else + local_status = PW_NOT_FORWARDING; + + /* local status didn't change */ + if (pw->local_status == local_status) + return (0); + pw->local_status = local_status; + + /* notify remote peer about the status update */ + ln = lde_nbr_find_by_lsrid(pw->lsr_id); + if (ln == NULL) + return (0); + l2vpn_pw_fec(pw, &fec); + if (pw->flags & F_PW_STATUSTLV) + l2vpn_send_pw_status(ln, local_status, &fec); + else { + struct fec_node *fn; + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn) { + if (pw->local_status == PW_FORWARDING) + lde_send_labelmapping(ln, fn, 1); + else + lde_send_labelwithdraw(ln, fn, NULL, NULL); + } + } + + return (0); +} + void l2vpn_pw_ctl(pid_t pid) { @@ -490,7 +549,9 @@ l2vpn_pw_ctl(pid_t pid) sizeof(pwctl.ifname)); pwctl.pwid = pw->pwid; pwctl.lsr_id = pw->lsr_id; - pwctl.status = pw->flags & F_PW_STATUS_UP; + if (pw->local_status == PW_FORWARDING && + pw->remote_status == PW_FORWARDING) + pwctl.status = 1; lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0, pid, &pwctl, sizeof(pwctl)); diff --git a/ldpd/lde.c b/ldpd/lde.c index 7540fc1cb..37d64eaf5 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -491,6 +491,15 @@ lde_dispatch_parent(struct thread *thread) } } break; + case IMSG_PW_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct zapi_pw_status)) + fatalx("PW_UPDATE imsg with wrong len"); + + if (l2vpn_pw_status_update(imsg.data) != 0) + log_warnx("%s: error updating PW status", + __func__); + break; case IMSG_NETWORK_ADD: case IMSG_NETWORK_UPDATE: if (imsg.hdr.len != IMSG_HEADER_SIZE + @@ -730,8 +739,8 @@ lde_update_label(struct fec_node *fn) void lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) { - struct kroute kr; - struct kpw kpw; + struct kroute kr; + struct zapi_pw zpw; struct l2vpn_pw *pw; switch (fn->fec.type) { @@ -769,19 +778,10 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) return; pw = (struct l2vpn_pw *) fn->data; - pw->flags |= F_PW_STATUS_UP; - - memset(&kpw, 0, sizeof(kpw)); - kpw.ifindex = pw->ifindex; - kpw.pw_type = fn->fec.u.pwid.type; - kpw.af = pw->af; - kpw.nexthop = pw->addr; - kpw.local_label = fn->local_label; - kpw.remote_label = fnh->remote_label; - kpw.flags = pw->flags; - - lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw, - sizeof(kpw)); + pw2zpw(pw, &zpw); + zpw.local_label = fn->local_label; + zpw.remote_label = fnh->remote_label; + lde_imsg_compose_parent(IMSG_KPW_SET, 0, &zpw, sizeof(zpw)); break; } } @@ -790,7 +790,7 @@ void lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) { struct kroute kr; - struct kpw kpw; + struct zapi_pw zpw; struct l2vpn_pw *pw; switch (fn->fec.type) { @@ -824,21 +824,10 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; - if (!(pw->flags & F_PW_STATUS_UP)) - return; - pw->flags &= ~F_PW_STATUS_UP; - - memset(&kpw, 0, sizeof(kpw)); - kpw.ifindex = pw->ifindex; - kpw.pw_type = fn->fec.u.pwid.type; - kpw.af = pw->af; - kpw.nexthop = pw->addr; - kpw.local_label = fn->local_label; - kpw.remote_label = fnh->remote_label; - kpw.flags = pw->flags; - - lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw, - sizeof(kpw)); + pw2zpw(pw, &zpw); + zpw.local_label = fn->local_label; + zpw.remote_label = fnh->remote_label; + lde_imsg_compose_parent(IMSG_KPW_UNSET, 0, &zpw, sizeof(zpw)); break; } } @@ -919,8 +908,12 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) */ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); if (lw) { - if (!fec_find(&ln->sent_map_pending, &fn->fec)) + if (!fec_find(&ln->sent_map_pending, &fn->fec)) { + debug_evt("%s: FEC %s: scheduling to send label " + "mapping later (waiting for pending label release)", + __func__, log_fec(&fn->fec)); lde_map_pending_add(ln, fn); + } return; } @@ -966,8 +959,7 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) map.flags |= F_MAP_PW_CWORD; if (pw->flags & F_PW_STATUSTLV) { map.flags |= F_MAP_PW_STATUS; - /* VPLS are always up */ - map.pw_status = PW_FORWARDING; + map.pw_status = pw->local_status; } break; } diff --git a/ldpd/lde.h b/ldpd/lde.h index 1cce48383..43f1d3648 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -238,6 +238,7 @@ void l2vpn_send_pw_status_wcard(struct lde_nbr *, uint32_t, void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *); void l2vpn_recv_pw_status_wcard(struct lde_nbr *, struct notify_msg *); +int l2vpn_pw_status_update(struct zapi_pw_status *); void l2vpn_pw_ctl(pid_t); void l2vpn_binding_ctl(pid_t); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 37a670bc8..c24a57b56 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -396,7 +396,7 @@ lde_kernel_update(struct fec *fec) lde_gc_start_timer(); } else { fn->local_label = lde_update_label(fn); - if (fn->local_label != NO_LABEL && RB_EMPTY(&fn->upstream)) + if (fn->local_label != NO_LABEL) /* FEC.1: perform lsr label distribution procedure */ RB_FOREACH(ln, nbr_tree, &lde_nbrs) lde_send_labelmapping(ln, fn, 1); @@ -531,6 +531,8 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) pw->remote_mtu = map->fec.pwid.ifmtu; if (map->flags & F_MAP_PW_STATUS) pw->remote_status = map->pw_status; + else + pw->remote_status = PW_FORWARDING; fnh->remote_label = map->label; if (l2vpn_pw_ok(pw, fnh)) lde_send_change_klabel(fn, fnh); @@ -780,6 +782,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) pw = (struct l2vpn_pw *) fn->data; if (pw == NULL) continue; + pw->remote_status = PW_NOT_FORWARDING; break; default: break; @@ -808,6 +811,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) struct fec_node *fn; struct fec_nh *fnh; struct lde_map *me; + struct l2vpn_pw *pw; /* LWd.2: send label release */ lde_send_labelrelease(ln, NULL, map, map->label); @@ -831,6 +835,9 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) case FEC_TYPE_PWID: if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr) continue; + pw = (struct l2vpn_pw *) fn->data; + if (pw) + pw->remote_status = PW_NOT_FORWARDING; break; default: break; diff --git a/ldpd/ldp.h b/ldpd/ldp.h index c2b64d20c..cac3da7c5 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -285,9 +285,6 @@ struct address_list_tlv { #define MAP_TYPE_GENPWID 0x81 #define CONTROL_WORD_FLAG 0x8000 -#define PW_TYPE_ETHERNET_TAGGED 0x0004 -#define PW_TYPE_ETHERNET 0x0005 -#define PW_TYPE_WILDCARD 0x7FFF #define DEFAULT_PW_TYPE PW_TYPE_ETHERNET #define PW_TWCARD_RESERVED_BIT 0x8000 diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 3320238a0..f7d715e81 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -54,6 +54,8 @@ static int ldp_interface_address_delete(int, struct zclient *, zebra_size_t, vrf_id_t); static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t, vrf_id_t); +static int ldp_zebra_read_pw_status_update(int, struct zclient *, + zebra_size_t, vrf_id_t); static void ldp_zebra_connected(struct zclient *); static struct zclient *zclient; @@ -94,6 +96,25 @@ ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka) } } +void +pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) +{ + memset(zpw, 0, sizeof(*zpw)); + strlcpy(zpw->ifname, pw->ifname, sizeof(zpw->ifname)); + zpw->ifindex = pw->ifindex; + zpw->type = pw->l2vpn->pw_type; + zpw->af = pw->af; + zpw->nexthop.ipv6 = pw->addr.v6; + zpw->local_label = NO_LABEL; + zpw->remote_label = NO_LABEL; + if (pw->flags & F_PW_CWORD) + zpw->flags = F_PSEUDOWIRE_CWORD; + zpw->data.ldp.lsr_id = pw->lsr_id; + zpw->data.ldp.pwid = pw->pwid; + strlcpy(zpw->data.ldp.vpn_name, pw->l2vpn->name, + sizeof(zpw->data.ldp.vpn_name)); +} + static int zebra_send_mpls_labels(int cmd, struct kroute *kr) { @@ -154,17 +175,40 @@ kr_delete(struct kroute *kr) } int -kmpw_set(struct kpw *kpw) +kmpw_add(struct zapi_pw *zpw) { - /* TODO */ - return (0); + debug_zebra_out("pseudowire %s nexthop %s (add)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + + return (zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw)); } int -kmpw_unset(struct kpw *kpw) +kmpw_del(struct zapi_pw *zpw) { - /* TODO */ - return (0); + debug_zebra_out("pseudowire %s nexthop %s (del)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + + return (zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw)); +} + +int +kmpw_set(struct zapi_pw *zpw) +{ + debug_zebra_out("pseudowire %s nexthop %s labels %u/%u (set)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop), + zpw->local_label, zpw->remote_label); + + return (zebra_send_pw(zclient, ZEBRA_PW_SET, zpw)); +} + +int +kmpw_unset(struct zapi_pw *zpw) +{ + debug_zebra_out("pseudowire %s nexthop %s (unset)", + zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + + return (zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw)); } void @@ -466,6 +510,25 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, return (0); } +/* + * Receive PW status update from Zebra and send it to LDE process. + */ +static int +ldp_zebra_read_pw_status_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct zapi_pw_status zpw; + + zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw); + + debug_zebra_in("pseudowire %s status %s", zpw.ifname, + (zpw.status == PW_STATUS_UP) ? "up" : "down"); + + main_imsg_compose_lde(IMSG_PW_UPDATE, 0, &zpw, sizeof(zpw)); + + return (0); +} + static void ldp_zebra_connected(struct zclient *zclient) { @@ -496,6 +559,7 @@ ldp_zebra_init(struct thread_master *master) zclient->redistribute_route_ipv4_del = ldp_zebra_read_route; zclient->redistribute_route_ipv6_add = ldp_zebra_read_route; zclient->redistribute_route_ipv6_del = ldp_zebra_read_route; + zclient->pw_status_update = ldp_zebra_read_pw_status_update; } void diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index f9e44012e..303baf463 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -591,21 +591,36 @@ main_dispatch_lde(struct thread *thread) if (kr_delete(imsg.data)) log_warnx("%s: error deleting route", __func__); break; - case IMSG_KPWLABEL_CHANGE: + case IMSG_KPW_ADD: + case IMSG_KPW_DELETE: + case IMSG_KPW_SET: + case IMSG_KPW_UNSET: if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct kpw)) + sizeof(struct zapi_pw)) fatalx("invalid size of IMSG_KPWLABEL_CHANGE"); - if (kmpw_set(imsg.data)) - log_warnx("%s: error changing pseudowire", - __func__); - break; - case IMSG_KPWLABEL_DELETE: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct kpw)) - fatalx("invalid size of IMSG_KPWLABEL_DELETE"); - if (kmpw_unset(imsg.data)) - log_warnx("%s: error unsetting pseudowire", - __func__); + + switch (imsg.hdr.type) { + case IMSG_KPW_ADD: + if (kmpw_add(imsg.data)) + log_warnx("%s: error adding " + "pseudowire", __func__); + break; + case IMSG_KPW_DELETE: + if (kmpw_del(imsg.data)) + log_warnx("%s: error deleting " + "pseudowire", __func__); + break; + case IMSG_KPW_SET: + if (kmpw_set(imsg.data)) + log_warnx("%s: error setting " + "pseudowire", __func__); + break; + case IMSG_KPW_UNSET: + if (kmpw_unset(imsg.data)) + log_warnx("%s: error unsetting " + "pseudowire", __func__); + break; + } break; case IMSG_ACL_CHECK: if (imsg.hdr.len != IMSG_HEADER_SIZE + diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 97239ed08..fd7d5c572 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -29,6 +29,8 @@ #include "qobj.h" #include "prefix.h" #include "filter.h" +#include "pw.h" +#include "zclient.h" #include "ldp.h" @@ -43,7 +45,6 @@ #define LDPD_OPT_NOACTION 0x00000004 #define TCP_MD5_KEY_LEN 80 -#define L2VPN_NAME_LEN 32 #define RT_BUF_SIZE 16384 #define MAX_RTSOCK_BUF 128 * 1024 @@ -101,8 +102,10 @@ enum imsg_type { IMSG_CTL_LOG_VERBOSE, IMSG_KLABEL_CHANGE, IMSG_KLABEL_DELETE, - IMSG_KPWLABEL_CHANGE, - IMSG_KPWLABEL_DELETE, + IMSG_KPW_ADD, + IMSG_KPW_DELETE, + IMSG_KPW_SET, + IMSG_KPW_UNSET, IMSG_IFSTATUS, IMSG_NEWADDR, IMSG_DELADDR, @@ -148,7 +151,8 @@ enum imsg_type { IMSG_ACL_CHECK, IMSG_GET_LABEL_CHUNK, IMSG_RELEASE_LABEL_CHUNK, - IMSG_INIT + IMSG_INIT, + IMSG_PW_UPDATE }; struct ldpd_init { @@ -408,6 +412,7 @@ struct l2vpn_pw { unsigned int ifindex; uint32_t remote_group; uint16_t remote_mtu; + uint32_t local_status; uint32_t remote_status; uint8_t flags; QOBJ_FIELDS @@ -419,8 +424,7 @@ DECLARE_QOBJ_TYPE(l2vpn_pw) #define F_PW_STATUSTLV 0x02 /* status tlv negotiated */ #define F_PW_CWORD_CONF 0x04 /* control word configured */ #define F_PW_CWORD 0x08 /* control word negotiated */ -#define F_PW_STATUS_UP 0x10 /* pseudowire is operational */ -#define F_PW_STATIC_NBR_ADDR 0x20 /* static neighbor address configured */ +#define F_PW_STATIC_NBR_ADDR 0x10 /* static neighbor address configured */ struct l2vpn { RB_ENTRY(l2vpn) entry; @@ -544,16 +548,6 @@ struct kroute { uint16_t flags; }; -struct kpw { - unsigned short ifindex; - int pw_type; - int af; - union ldpd_addr nexthop; - uint32_t local_label; - uint32_t remote_label; - uint8_t flags; -}; - struct kaddr { char ifname[IF_NAMESIZE]; unsigned short ifindex; @@ -670,11 +664,14 @@ struct ldpd_conf *parse_config(char *); int cmdline_symset(char *); /* kroute.c */ +void pw2zpw(struct l2vpn_pw *, struct zapi_pw *); void kif_redistribute(const char *); int kr_change(struct kroute *); int kr_delete(struct kroute *); -int kmpw_set(struct kpw *); -int kmpw_unset(struct kpw *); +int kmpw_add(struct zapi_pw *); +int kmpw_del(struct zapi_pw *); +int kmpw_set(struct zapi_pw *); +int kmpw_unset(struct zapi_pw *); /* util.c */ uint8_t mask2prefixlen(in_addr_t); diff --git a/lib/Makefile.am b/lib/Makefile.am index 14b7130c8..1f3180697 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -73,6 +73,7 @@ pkginclude_HEADERS = \ module.h \ hook.h \ libfrr.h \ + pw.h \ # end noinst_HEADERS = \ diff --git a/lib/command.c b/lib/command.c index 4912461ad..c2ee79035 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1392,6 +1392,7 @@ cmd_exit (struct vty *vty) vty_config_unlock (vty); break; case INTERFACE_NODE: + case PW_NODE: case NS_NODE: case VRF_NODE: case ZEBRA_NODE: @@ -1471,6 +1472,7 @@ DEFUN (config_end, break; case CONFIG_NODE: case INTERFACE_NODE: + case PW_NODE: case NS_NODE: case VRF_NODE: case ZEBRA_NODE: diff --git a/lib/command.h b/lib/command.h index 223f02814..313d73f7c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -130,6 +130,7 @@ enum node_type FORWARDING_NODE, /* IP forwarding node. */ PROTOCOL_NODE, /* protocol filtering node */ MPLS_NODE, /* MPLS config node */ + PW_NODE, /* Pseudowire config node */ VTY_NODE, /* Vty node. */ LINK_PARAMS_NODE, /* Link-parameters node */ }; @@ -937,6 +937,11 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_LABEL_MANAGER_CONNECT), DESC_ENTRY (ZEBRA_GET_LABEL_CHUNK), DESC_ENTRY (ZEBRA_RELEASE_LABEL_CHUNK), + DESC_ENTRY (ZEBRA_PW_ADD), + DESC_ENTRY (ZEBRA_PW_DELETE), + DESC_ENTRY (ZEBRA_PW_SET), + DESC_ENTRY (ZEBRA_PW_UNSET), + DESC_ENTRY (ZEBRA_PW_STATUS_UPDATE), }; #undef DESC_ENTRY diff --git a/lib/pw.h b/lib/pw.h new file mode 100644 index 000000000..63d8e52dd --- /dev/null +++ b/lib/pw.h @@ -0,0 +1,53 @@ +/* Pseudowire definitions + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _FRR_PW_H +#define _FRR_PW_H + +/* L2VPN name length. */ +#define L2VPN_NAME_LEN 32 + +/* Pseudowire type - LDP and BGP use the same values. */ +#define PW_TYPE_ETHERNET_TAGGED 0x0004 /* RFC 4446 */ +#define PW_TYPE_ETHERNET 0x0005 /* RFC 4446 */ +#define PW_TYPE_WILDCARD 0x7FFF /* RFC 4863, RFC 6668 */ + +/* Pseudowire flags. */ +#define F_PSEUDOWIRE_CWORD 0x01 + +/* Pseudowire status. */ +#define PW_STATUS_DOWN 0 +#define PW_STATUS_UP 1 + +/* + * Protocol-specific information about the pseudowire. + */ +union pw_protocol_fields +{ + struct { + struct in_addr lsr_id; + uint32_t pwid; + char vpn_name[L2VPN_NAME_LEN]; + } ldp; + struct { + /* TODO */ + } bgp; +}; + +#endif /* _FRR_PW_H */ @@ -732,6 +732,7 @@ vty_end_config (struct vty *vty) break; case CONFIG_NODE: case INTERFACE_NODE: + case PW_NODE: case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: @@ -1157,6 +1158,7 @@ vty_stop_input (struct vty *vty) break; case CONFIG_NODE: case INTERFACE_NODE: + case PW_NODE: case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: diff --git a/lib/zclient.c b/lib/zclient.c index 6aea4bd0a..9338f9c98 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1715,6 +1715,72 @@ lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end) return 0; } +int +zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) +{ + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, command, VRF_DEFAULT); + stream_write(s, pw->ifname, IF_NAMESIZE); + stream_putl(s, pw->ifindex); + + /* Put type */ + stream_putl(s, pw->type); + + /* Put nexthop */ + stream_putl(s, pw->af); + switch (pw->af) + { + case AF_INET: + stream_put_in_addr(s, &pw->nexthop.ipv4); + break; + case AF_INET6: + stream_write(s, (u_char *)&pw->nexthop.ipv6, 16); + break; + default: + zlog_err ("%s: unknown af", __func__); + return -1; + } + + /* Put labels */ + stream_putl(s, pw->local_label); + stream_putl(s, pw->remote_label); + + /* Put flags */ + stream_putc(s, pw->flags); + + /* Protocol specific fields */ + stream_write(s, &pw->data, sizeof(union pw_protocol_fields)); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + +/* + * Receive PW status update from Zebra and send it to LDE process. + */ +void +zebra_read_pw_status_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id, + struct zapi_pw_status *pw) +{ + struct stream *s; + + memset(pw, 0, sizeof(struct zapi_pw_status)); + s = zclient->ibuf; + + /* Get data. */ + stream_get(pw->ifname, s, IF_NAMESIZE); + pw->ifindex = stream_getl(s); + pw->status = stream_getl(s); +} + /* Zebra client message read function. */ static int zclient_read (struct thread *thread) @@ -1899,6 +1965,10 @@ zclient_read (struct thread *thread) if (zclient->interface_link_params) (*zclient->interface_link_params) (command, zclient, length); break; + case ZEBRA_PW_STATUS_UPDATE: + if (zclient->pw_status_update) + (*zclient->pw_status_update) (command, zclient, length, vrf_id); + break; default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index d3d0a202c..489ed24c8 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -31,6 +31,12 @@ /* For vrf_bitmap_t. */ #include "vrf.h" +/* For union g_addr */ +#include "nexthop.h" + +/* For union pw_protocol_fields */ +#include "pw.h" + /* For input/output buffer to zebra. */ #define ZEBRA_MAX_PACKET_SIZ 4096 @@ -94,6 +100,11 @@ typedef enum { ZEBRA_LABEL_MANAGER_CONNECT, ZEBRA_GET_LABEL_CHUNK, ZEBRA_RELEASE_LABEL_CHUNK, + ZEBRA_PW_ADD, + ZEBRA_PW_DELETE, + ZEBRA_PW_SET, + ZEBRA_PW_UNSET, + ZEBRA_PW_STATUS_UPDATE, } zebra_message_types_t; struct redist_proto @@ -164,6 +175,7 @@ struct zclient int (*redistribute_route_ipv4_del) (int, struct zclient *, uint16_t, vrf_id_t); int (*redistribute_route_ipv6_add) (int, struct zclient *, uint16_t, vrf_id_t); int (*redistribute_route_ipv6_del) (int, struct zclient *, uint16_t, vrf_id_t); + int (*pw_status_update) (int, struct zclient *, uint16_t, vrf_id_t); }; /* Zebra API message flag. */ @@ -217,6 +229,27 @@ struct zapi_ipv4 vrf_id_t vrf_id; }; +struct zapi_pw +{ + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + int type; + int af; + union g_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + union pw_protocol_fields data; + uint8_t protocol; +}; + +struct zapi_pw_status +{ + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + uint32_t status; +}; + /* Prototypes of zebra client service functions. */ extern struct zclient *zclient_new (struct thread_master *); extern void zclient_init (struct zclient *, int, u_short); @@ -278,6 +311,11 @@ extern int lm_label_manager_connect (struct zclient *zclient); extern int lm_get_label_chunk (struct zclient *zclient, u_char keep, uint32_t chunk_size, uint32_t *start, uint32_t *end); extern int lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end); +extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); +extern void zebra_read_pw_status_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id, + struct zapi_pw_status *pw); + /* IPv6 prefix add and delete function prototype. */ struct zapi_ipv6 diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 9a5008f9d..2e928657f 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -127,6 +127,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/zebra/zebra_fpm.c \ $(top_srcdir)/zebra/zebra_ptm.c \ $(top_srcdir)/zebra/zebra_mpls_vty.c \ + $(top_srcdir)/zebra/zebra_pw.c \ $(top_srcdir)/watchfrr/watchfrr_vty.c \ $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 17b95707d..84907fb63 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -890,6 +890,12 @@ static struct cmd_node interface_node = "%s(config-if)# ", }; +static struct cmd_node pw_node = +{ + PW_NODE, + "%s(config-pw)# ", +}; + static struct cmd_node ns_node = { NS_NODE, @@ -1489,6 +1495,7 @@ vtysh_exit (struct vty *vty) vty->node = ENABLE_NODE; break; case INTERFACE_NODE: + case PW_NODE: case NS_NODE: case VRF_NODE: case ZEBRA_NODE: @@ -1776,6 +1783,17 @@ DEFUNSH (VTYSH_INTERFACE, return CMD_SUCCESS; } +DEFUNSH (VTYSH_ZEBRA, + vtysh_pseudowire, + vtysh_pseudowire_cmd, + "pseudowire IFNAME", + "Static pseudowire configuration\n" + "Pseudowire name\n") +{ + vty->node = PW_NODE; + return CMD_SUCCESS; +} + /* TODO Implement "no interface command in isisd. */ DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D, vtysh_no_interface_cmd, @@ -3094,6 +3112,7 @@ vtysh_init_vty (void) install_node (&bgp_node, NULL); install_node (&rip_node, NULL); install_node (&interface_node, NULL); + install_node (&pw_node, NULL); install_node (&link_params_node, NULL); install_node (&ns_node, NULL); install_node (&vrf_node, NULL); @@ -3130,6 +3149,7 @@ vtysh_init_vty (void) vtysh_install_default (BGP_NODE); vtysh_install_default (RIP_NODE); vtysh_install_default (INTERFACE_NODE); + vtysh_install_default (PW_NODE); vtysh_install_default (LINK_PARAMS_NODE); vtysh_install_default (NS_NODE); vtysh_install_default (VRF_NODE); @@ -3273,6 +3293,10 @@ vtysh_init_vty (void) install_element (LINK_PARAMS_NODE, &vtysh_exit_interface_cmd); install_element (INTERFACE_NODE, &vtysh_quit_interface_cmd); + install_element (PW_NODE, &vtysh_end_all_cmd); + install_element (PW_NODE, &vtysh_exit_interface_cmd); + install_element (PW_NODE, &vtysh_quit_interface_cmd); + install_element (NS_NODE, &vtysh_end_all_cmd); install_element (CONFIG_NODE, &vtysh_ns_cmd); @@ -3335,6 +3359,7 @@ vtysh_init_vty (void) install_element (CONFIG_NODE, &vtysh_interface_cmd); install_element (CONFIG_NODE, &vtysh_no_interface_cmd); install_element (CONFIG_NODE, &vtysh_no_interface_vrf_cmd); + install_element (CONFIG_NODE, &vtysh_pseudowire_cmd); install_element (INTERFACE_NODE, &vtysh_link_params_cmd); install_element (ENABLE_NODE, &vtysh_show_running_config_cmd); install_element (ENABLE_NODE, &vtysh_copy_running_config_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 94c4042dd..4e4c24112 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -196,6 +196,8 @@ vtysh_config_parse_line (const char *line) default: if (strncmp (line, "interface", strlen ("interface")) == 0) config = config_get (INTERFACE_NODE, line); + else if (strncmp (line, "pseudowire", strlen ("pseudowire")) == 0) + config = config_get (PW_NODE, line); else if (strncmp (line, "logical-router", strlen ("ns")) == 0) config = config_get (NS_NODE, line); else if (strncmp (line, "vrf", strlen ("vrf")) == 0) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 3e0de3b46..9e9998ebb 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -33,14 +33,14 @@ zebra_SOURCES = \ zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \ zebra_mroute.c \ - label_manager.c \ + label_manager.c zebra_pw.c \ # end testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \ zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ - zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c + zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c zebra_pw_null.c noinst_HEADERS = \ zebra_memory.h \ @@ -49,7 +49,8 @@ noinst_HEADERS = \ rt_netlink.h zebra_fpm_private.h zebra_rnh.h \ zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \ zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \ - kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h + kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h zebra_pw.h \ + # end zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP) diff --git a/zebra/debug.c b/zebra/debug.c index a42d5aa3e..6f59dc0ac 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -32,6 +32,7 @@ unsigned long zebra_debug_rib; unsigned long zebra_debug_fpm; unsigned long zebra_debug_nht; unsigned long zebra_debug_mpls; +unsigned long zebra_debug_pw; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, @@ -85,6 +86,8 @@ DEFUN (show_debugging_zebra, vty_out (vty, " Zebra next-hop tracking debugging is on%s", VTY_NEWLINE); if (IS_ZEBRA_DEBUG_MPLS) vty_out (vty, " Zebra MPLS debugging is on%s", VTY_NEWLINE); + if (IS_ZEBRA_DEBUG_PW) + vty_out (vty, " Zebra pseudowire debugging is on%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -122,6 +125,21 @@ DEFUN (debug_zebra_mpls, return CMD_WARNING; } +DEFUN (debug_zebra_pw, + debug_zebra_pw_cmd, + "[no] debug zebra pseudowires", + "Negate a command or set its defaults\n" + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra pseudowires\n") +{ + if (strmatch (argv[0]->text, "no")) + UNSET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW); + else + SET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW); + return CMD_WARNING; +} + DEFUN (debug_zebra_packet, debug_zebra_packet_cmd, "debug zebra packet [<recv|send>] [detail]", @@ -410,6 +428,11 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug zebra mpls%s", VTY_NEWLINE); write++; } + if (IS_ZEBRA_DEBUG_PW) + { + vty_out (vty, "debug zebra pseudowires%s", VTY_NEWLINE); + write++; + } return write; } @@ -422,6 +445,7 @@ zebra_debug_init (void) zebra_debug_rib = 0; zebra_debug_fpm = 0; zebra_debug_mpls = 0; + zebra_debug_pw = 0; install_node (&debug_node, config_write_debug); @@ -430,6 +454,7 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &debug_zebra_events_cmd); install_element (ENABLE_NODE, &debug_zebra_nht_cmd); install_element (ENABLE_NODE, &debug_zebra_mpls_cmd); + install_element (ENABLE_NODE, &debug_zebra_pw_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_cmd); install_element (ENABLE_NODE, &debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd); @@ -449,6 +474,7 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_events_cmd); install_element (CONFIG_NODE, &debug_zebra_nht_cmd); install_element (CONFIG_NODE, &debug_zebra_mpls_cmd); + install_element (CONFIG_NODE, &debug_zebra_pw_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); install_element (CONFIG_NODE, &debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index f8ebf3d61..9a196d2f3 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -43,6 +43,8 @@ #define ZEBRA_DEBUG_MPLS 0x01 +#define ZEBRA_DEBUG_PW 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -64,6 +66,7 @@ #define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM) #define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT) #define IS_ZEBRA_DEBUG_MPLS (zebra_debug_mpls & ZEBRA_DEBUG_MPLS) +#define IS_ZEBRA_DEBUG_PW (zebra_debug_pw & ZEBRA_DEBUG_PW) extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; @@ -72,6 +75,7 @@ extern unsigned long zebra_debug_rib; extern unsigned long zebra_debug_fpm; extern unsigned long zebra_debug_nht; extern unsigned long zebra_debug_mpls; +extern unsigned long zebra_debug_pw; extern void zebra_debug_init (void); diff --git a/zebra/main.c b/zebra/main.c index 459e6148d..84d71c33b 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -314,6 +314,7 @@ main (int argc, char **argv) zebra_mpls_init (); zebra_mpls_vty_init (); + zebra_pw_vty_init (); /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/rib.h b/zebra/rib.h index 5381d76b9..e910facb4 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -91,6 +91,7 @@ struct rib #define RIB_ENTRY_NEXTHOPS_CHANGED 0x2 #define RIB_ENTRY_CHANGED 0x4 #define RIB_ENTRY_SELECTED_FIB 0x8 +#define RIB_ENTRY_LABELS_CHANGED 0x10 /* Nexthop information. */ u_char nexthop_num; diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5a3ed7545..ba500cac2 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1346,7 +1346,7 @@ mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type, return 0; SET_FLAG (rib->status, RIB_ENTRY_CHANGED); - SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); rib_queue_add (rn); return 0; @@ -1542,7 +1542,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi) { nexthop_del_labels (nexthop); SET_FLAG (rib->status, RIB_ENTRY_CHANGED); - SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); update = 1; } diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 5dfe16caf..0fcd28e0e 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -35,6 +35,7 @@ extern struct zebra_privs_t zserv_privs; struct { u_int32_t rtseq; int fd; + int ioctl_fd; } kr_state; static int @@ -337,6 +338,90 @@ kernel_del_lsp (zebra_lsp_t *lsp) return ret; } +static int +kmpw_install (struct zebra_pw *pw) +{ + struct ifreq ifr; + struct ifmpwreq imr; + struct sockaddr_storage ss; + struct sockaddr_in *sa_in = (struct sockaddr_in *) &ss; + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) &ss; + + memset (&imr, 0, sizeof (imr)); + switch (pw->type) + { + case PW_TYPE_ETHERNET: + imr.imr_type = IMR_TYPE_ETHERNET; + break; + case PW_TYPE_ETHERNET_TAGGED: + imr.imr_type = IMR_TYPE_ETHERNET_TAGGED; + break; + default: + zlog_err ("%s: unhandled pseudowire type (%#X)", __func__, + pw->type); + return -1; + } + + if (pw->flags & F_PSEUDOWIRE_CWORD) + imr.imr_flags |= IMR_FLAG_CONTROLWORD; + + /* pseudowire nexthop */ + memset (&ss, 0, sizeof (ss)); + switch (pw->af) { + case AF_INET: + sa_in->sin_family = AF_INET; + sa_in->sin_len = sizeof (struct sockaddr_in); + sa_in->sin_addr = pw->nexthop.ipv4; + break; + case AF_INET6: + sa_in6->sin6_family = AF_INET6; + sa_in6->sin6_len = sizeof (struct sockaddr_in6); + sa_in6->sin6_addr = pw->nexthop.ipv6; + break; + default: + zlog_err ("%s: unhandled pseudowire address-family (%u)", __func__, + pw->af); + return -1; + } + memcpy (&imr.imr_nexthop, (struct sockaddr *) &ss, + sizeof (imr.imr_nexthop)); + + /* pseudowire local/remote labels */ + imr.imr_lshim.shim_label = pw->local_label; + imr.imr_rshim.shim_label = pw->remote_label; + + /* ioctl */ + memset (&ifr, 0, sizeof (ifr)); + strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name)); + ifr.ifr_data = (caddr_t) &imr; + if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) + { + zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno)); + return -1; + } + + return 0; +} + +static int +kmpw_uninstall (struct zebra_pw *pw) +{ + struct ifreq ifr; + struct ifmpwreq imr; + + memset(&ifr, 0, sizeof (ifr)); + memset(&imr, 0, sizeof (imr)); + strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name)); + ifr.ifr_data = (caddr_t) &imr; + if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) + { + zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno)); + return -1; + } + + return 0; +} + #define MAX_RTSOCK_BUF 128 * 1024 int mpls_kernel_init (void) @@ -344,10 +429,17 @@ mpls_kernel_init (void) int rcvbuf, default_rcvbuf; socklen_t optlen; - if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) + { zlog_warn("%s: socket", __func__); return -1; - } + } + + if ((kr_state.ioctl_fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) + { + zlog_warn("%s: ioctl socket", __func__); + return -1; + } /* grow receive buffer, don't wanna miss messages */ optlen = sizeof (default_rcvbuf); @@ -364,5 +456,9 @@ mpls_kernel_init (void) kr_state.rtseq = 1; + /* register hook to install/uninstall pseudowires */ + hook_register (pw_install, kmpw_install); + hook_register (pw_uninstall, kmpw_uninstall); + return 0; } diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 000000000..2164ddf6e --- /dev/null +++ b/zebra/zebra_pw.c @@ -0,0 +1,532 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "thread.h" +#include "command.h" +#include "vrf.h" + +#include "zebra/debug.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_pw.h" + +DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire") + +DEFINE_QOBJ_TYPE(zebra_pw) + +DEFINE_HOOK(pw_install, (struct zebra_pw *pw), (pw)) +DEFINE_HOOK(pw_uninstall, (struct zebra_pw *pw), (pw)) + +extern struct zebra_t zebrad; + +static int zebra_pw_enabled(struct zebra_pw *); +static void zebra_pw_install(struct zebra_pw *); +static void zebra_pw_uninstall(struct zebra_pw *); +static int zebra_pw_install_retry(struct thread *); +static int zebra_pw_check_reachability(struct zebra_pw *); +static void zebra_pw_update_status(struct zebra_pw *, int); + +static inline int zebra_pw_compare(const struct zebra_pw *a, + const struct zebra_pw *b) +{ + return (strcmp(a->ifname, b->ifname)); +} + +RB_GENERATE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare) +RB_GENERATE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, + uint8_t protocol, struct zserv *client) +{ + struct zebra_pw *pw; + + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: adding pseudowire %s protocol %s", + zvrf_id(zvrf), ifname, zebra_route_string(protocol)); + + pw = XCALLOC(MTYPE_PW, sizeof(*pw)); + strlcpy(pw->ifname, ifname, sizeof(pw->ifname)); + pw->protocol = protocol; + pw->vrf_id = zvrf_id(zvrf); + pw->client = client; + pw->status = PW_STATUS_UP; + pw->local_label = MPLS_NO_LABEL; + pw->remote_label = MPLS_NO_LABEL; + pw->flags = F_PSEUDOWIRE_CWORD; + + RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw); + if (pw->protocol == ZEBRA_ROUTE_STATIC) { + RB_INSERT(zebra_static_pw_head, &zvrf->static_pseudowires, pw); + QOBJ_REG(pw, zebra_pw); + } + + return pw; +} + +void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id, + pw->ifname, zebra_route_string(pw->protocol)); + + /* remove nexthop tracking */ + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + + /* uninstall */ + if (pw->status == PW_STATUS_UP) + hook_call(pw_uninstall, pw); + else if (pw->install_retry_timer) + THREAD_TIMER_OFF(pw->install_retry_timer); + + /* unlink and release memory */ + RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw); + if (pw->protocol == ZEBRA_ROUTE_STATIC) + RB_REMOVE(zebra_static_pw_head, &zvrf->static_pseudowires, pw); + XFREE(MTYPE_PW, pw); +} + +void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, + union g_addr *nexthop, uint32_t local_label, + uint32_t remote_label, uint8_t flags, + union pw_protocol_fields *data) +{ + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + + pw->ifindex = ifindex; + pw->type = type; + pw->af = af; + pw->nexthop = *nexthop; + pw->local_label = local_label; + pw->remote_label = remote_label; + pw->flags = flags; + pw->data = *data; + + if (zebra_pw_enabled(pw)) + zebra_register_rnh_pseudowire(pw->vrf_id, pw); + else + zebra_pw_uninstall(pw); +} + +struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname) +{ + struct zebra_pw pw; + strlcpy(pw.ifname, ifname, sizeof(pw.ifname)); + return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw)); +} + +static int zebra_pw_enabled(struct zebra_pw *pw) +{ + if (pw->protocol == ZEBRA_ROUTE_STATIC) { + if (pw->local_label == MPLS_NO_LABEL + || pw->remote_label == MPLS_NO_LABEL + || pw->af == AF_UNSPEC) + return 0; + return 1; + } else + return pw->enabled; +} + +void zebra_pw_update(struct zebra_pw *pw) +{ + if (zebra_pw_check_reachability(pw) < 0) { + zebra_pw_uninstall(pw); + /* wait for NHT and try again later */ + } else { + /* + * Install or reinstall the pseudowire (e.g. to update + * parameters like the nexthop or the use of the control word). + */ + zebra_pw_install(pw); + } +} + +static void zebra_pw_install(struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: installing pseudowire %s protocol %s", + pw->vrf_id, pw->ifname, + zebra_route_string(pw->protocol)); + + if (hook_call(pw_install, pw)) { + zebra_pw_install_failure(pw); + return; + } + + if (pw->status == PW_STATUS_DOWN) + zebra_pw_update_status(pw, PW_STATUS_UP); +} + +static void zebra_pw_uninstall(struct zebra_pw *pw) +{ + if (pw->status == PW_STATUS_DOWN) + return; + + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: uninstalling pseudowire %s protocol %s", + pw->vrf_id, pw->ifname, + zebra_route_string(pw->protocol)); + + /* ignore any possible error */ + hook_call(pw_uninstall, pw); + + if (zebra_pw_enabled(pw)) + zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +/* + * Installation of the pseudowire in the kernel or hardware has failed. This + * function will notify the pseudowire client about the failure and schedule + * to retry the installation later. This function can be called by an external + * agent that performs the pseudowire installation in an asynchronous way. + */ +void zebra_pw_install_failure(struct zebra_pw *pw) +{ + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%u: failed installing pseudowire %s, " + "scheduling retry in %u seconds", pw->vrf_id, + pw->ifname, PW_INSTALL_RETRY_INTERVAL); + + /* schedule to retry later */ + THREAD_TIMER_OFF(pw->install_retry_timer); + pw->install_retry_timer = + thread_add_timer(zebrad.master, zebra_pw_install_retry, + pw, PW_INSTALL_RETRY_INTERVAL); + + zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +static int zebra_pw_install_retry(struct thread *thread) +{ + struct zebra_pw *pw = THREAD_ARG(thread); + + pw->install_retry_timer = NULL; + zebra_pw_install(pw); + + return 0; +} + +static void zebra_pw_update_status(struct zebra_pw *pw, int status) +{ + pw->status = status; + if (pw->client) + zsend_pw_update(pw->client, pw); +} + +static int zebra_pw_check_reachability(struct zebra_pw *pw) +{ + struct rib *rib; + struct nexthop *nexthop, *tnexthop; + int recursing; + + /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ + + /* find route to the remote end of the pseudowire */ + rib = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (!rib) { + if (IS_ZEBRA_DEBUG_PW) + zlog_warn("%s: no route found for %s", __func__, + pw->ifname); + return -1; + } + + /* + * Need to ensure that there's a label binding for all nexthops. + * Otherwise, ECMP for this route could render the pseudowire unusable. + */ + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (!nexthop->nh_label) { + if (IS_ZEBRA_DEBUG_PW) + zlog_warn("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + } + + return 0; +} + +void +zebra_pw_client_close(struct zserv *client) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + struct zebra_pw *pw, *tmp; + + RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + RB_FOREACH_SAFE(pw, zebra_pw_head, &zvrf->pseudowires, tmp) { + if (pw->client != client) + continue; + zebra_pw_del(zvrf, pw); + } + } +} + +void zebra_pw_init(struct zebra_vrf *zvrf) +{ + RB_INIT(&zvrf->pseudowires); + RB_INIT(&zvrf->static_pseudowires); +} + +void zebra_pw_exit(struct zebra_vrf *zvrf) +{ + struct zebra_pw *pw; + + while ((pw = RB_ROOT(&zvrf->pseudowires)) != NULL) + zebra_pw_del(zvrf, pw); +} + +DEFUN_NOSH (pseudowire_if, + pseudowire_if_cmd, + "[no] pseudowire IFNAME", + NO_STR + "Static pseudowire configuration\n" + "Pseudowire name\n") +{ + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + int idx = 0; + const char *ifname; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return CMD_WARNING; + + argv_find(argv, argc, "IFNAME", &idx); + ifname = argv[idx]->arg; + pw = zebra_pw_find(zvrf, ifname); + if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) { + vty_out(vty, "%% Pseudowire is not static%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv_find(argv, argc, "no", &idx)) { + if (!pw) + return CMD_SUCCESS; + zebra_pw_del(zvrf, pw); + } + + if (!pw) + pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL); + VTY_PUSH_CONTEXT(PW_NODE, pw); + + return CMD_SUCCESS; +} + +DEFUN (pseudowire_labels, + pseudowire_labels_cmd, + "[no] mpls label local (16-1048575) remote (16-1048575)", + NO_STR + "MPLS L2VPN PW command\n" + "MPLS L2VPN static labels\n" + "Local pseudowire label\n" + "Local pseudowire label\n" + "Remote pseudowire label\n" + "Remote pseudowire label\n") +{ + VTY_DECLVAR_CONTEXT(zebra_pw, pw); + int idx = 0; + mpls_label_t local_label, remote_label; + + if (argv_find(argv, argc, "no", &idx)) { + local_label = MPLS_NO_LABEL; + remote_label = MPLS_NO_LABEL; + } else { + argv_find(argv, argc, "local", &idx); + local_label = atoi(argv[idx + 1]->arg); + argv_find(argv, argc, "remote", &idx); + remote_label = atoi(argv[idx + 1]->arg); + } + + zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, + local_label, remote_label, pw->flags, &pw->data); + + return CMD_SUCCESS; +} + +DEFUN (pseudowire_neighbor, + pseudowire_neighbor_cmd, + "[no] neighbor <A.B.C.D|X:X::X:X>", + NO_STR + "Specify the IPv4 or IPv6 address of the remote endpoint\n" + "IPv4 address\n" + "IPv6 address\n") +{ + VTY_DECLVAR_CONTEXT(zebra_pw, pw); + int idx = 0; + const char *address; + int af; + union g_addr nexthop; + + af = AF_UNSPEC; + memset(&nexthop, 0, sizeof(nexthop)); + + if (!argv_find(argv, argc, "no", &idx)) { + argv_find(argv, argc, "neighbor", &idx); + address = argv[idx + 1]->arg; + + if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1) + af = AF_INET; + else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1) + af = AF_INET6; + else { + vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop, + pw->local_label, pw->remote_label, pw->flags, + &pw->data); + + return CMD_SUCCESS; +} + +DEFUN (pseudowire_control_word, + pseudowire_control_word_cmd, + "[no] control-word <exclude|include>", + NO_STR + "Control-word options\n" + "Exclude control-word in pseudowire packets\n" + "Include control-word in pseudowire packets\n") +{ + VTY_DECLVAR_CONTEXT(zebra_pw, pw); + int idx = 0; + uint8_t flags = 0; + + if (argv_find(argv, argc, "no", &idx)) + flags = F_PSEUDOWIRE_CWORD; + else { + argv_find(argv, argc, "control-word", &idx); + if (argv[idx + 1]->text[0] == 'i') + flags = F_PSEUDOWIRE_CWORD; + } + + zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, + pw->local_label, pw->remote_label, flags, &pw->data); + + return CMD_SUCCESS; +} + +DEFUN (show_pseudowires, + show_pseudowires_cmd, + "show pseudowires", + SHOW_STR + "Pseudowires") +{ + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s", + "Interface", "Neighbor", "Labels", "Protocol", "Status", + VTY_NEWLINE); + + RB_FOREACH(pw, zebra_pw_head, &zvrf->pseudowires) { + char buf_nbr[INET6_ADDRSTRLEN]; + char buf_labels[64]; + + inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + + if (pw->local_label != MPLS_NO_LABEL + && pw->remote_label != MPLS_NO_LABEL) + snprintf(buf_labels, sizeof(buf_labels), "%u/%u", + pw->local_label, pw->remote_label); + else + snprintf(buf_labels, sizeof(buf_labels), "-"); + + vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s", + pw->ifname, (pw->af != AF_UNSPEC) ? buf_nbr : "-", + buf_labels, zebra_route_string(pw->protocol), + (zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) ? + "UP" : "DOWN", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/* Pseudowire configuration write function. */ +static int zebra_pw_config(struct vty *vty) +{ + int write = 0; + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return 0; + + RB_FOREACH(pw, zebra_static_pw_head, &zvrf->static_pseudowires) { + vty_out(vty, "pseudowire %s%s", pw->ifname, VTY_NEWLINE); + if (pw->local_label != MPLS_NO_LABEL + && pw->remote_label != MPLS_NO_LABEL) + vty_out(vty, " mpls label local %u remote %u%s", + pw->local_label, pw->remote_label, + VTY_NEWLINE); + else + vty_out(vty, " ! Incomplete config, specify the static " + "MPLS labels%s", VTY_NEWLINE); + + if (pw->af != AF_UNSPEC) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf)); + vty_out(vty, " neighbor %s%s", buf, VTY_NEWLINE); + } else + vty_out(vty, " ! Incomplete config, specify a neighbor " + "address%s", VTY_NEWLINE); + + if (!(pw->flags & F_PSEUDOWIRE_CWORD)) + vty_out(vty, " control-word exclude%s", VTY_NEWLINE); + + vty_out(vty, "!%s", VTY_NEWLINE); + write = 1; + } + + return write; +} + +static struct cmd_node pw_node = +{ + PW_NODE, + "%s(config-pw)# ", + 1, +}; + +void zebra_pw_vty_init(void) +{ + install_node(&pw_node, zebra_pw_config); + install_default(PW_NODE); + + install_element(CONFIG_NODE, &pseudowire_if_cmd); + install_element(PW_NODE, &pseudowire_labels_cmd); + install_element(PW_NODE, &pseudowire_neighbor_cmd); + install_element(PW_NODE, &pseudowire_control_word_cmd); + + install_element(VIEW_NODE, &show_pseudowires_cmd); +} diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h new file mode 100644 index 000000000..b588bac0a --- /dev/null +++ b/zebra/zebra_pw.h @@ -0,0 +1,75 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef ZEBRA_PW_H_ +#define ZEBRA_PW_H_ + +#include <net/if.h> +#include <netinet/in.h> + +#include "hook.h" +#include "qobj.h" + +#define PW_INSTALL_RETRY_INTERVAL 30 + +struct zebra_pw { + RB_ENTRY(zebra_pw) pw_entry, static_pw_entry; + vrf_id_t vrf_id; + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + int type; + int af; + union g_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + union pw_protocol_fields data; + int enabled; + int status; + uint8_t protocol; + struct zserv *client; + struct rnh *rnh; + struct thread *install_retry_timer; + QOBJ_FIELDS +}; +DECLARE_QOBJ_TYPE(zebra_pw) + +RB_HEAD(zebra_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare); + +RB_HEAD(zebra_static_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare); + +DECLARE_HOOK(pw_install, (struct zebra_pw * pw), (pw)) +DECLARE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw)) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *, const char *, + uint8_t, struct zserv *); +void zebra_pw_del(struct zebra_vrf *, struct zebra_pw *); +void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *, + uint32_t, uint32_t, uint8_t, union pw_protocol_fields *); +struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *); +void zebra_pw_update(struct zebra_pw *); +void zebra_pw_install_failure(struct zebra_pw *); +void zebra_pw_client_close(struct zserv *); +void zebra_pw_init(struct zebra_vrf *); +void zebra_pw_exit(struct zebra_vrf *); +void zebra_pw_vty_init(void); + +#endif /* ZEBRA_PW_H_ */ diff --git a/zebra/zebra_pw_null.c b/zebra/zebra_pw_null.c new file mode 100644 index 000000000..d19d80b8c --- /dev/null +++ b/zebra/zebra_pw_null.c @@ -0,0 +1,29 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "vrf.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" + +void zebra_pw_init(struct zebra_vrf *zvrf) {} +void zebra_pw_exit(struct zebra_vrf *zvrf) {} diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 5096e9539..d5ebbbc46 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -131,6 +131,7 @@ zebra_add_rnh (struct prefix *p, vrf_id_t vrfid, rnh_type_t type) rnh->client_list = list_new(); rnh->vrf_id = vrfid; rnh->zebra_static_route_list = list_new(); + rnh->zebra_pseudowire_list = list_new(); route_lock_node (rn); rn->info = rnh; rnh->node = rn; @@ -168,6 +169,7 @@ zebra_free_rnh (struct rnh *rnh) rnh->flags |= ZEBRA_NHT_DELETED; list_free (rnh->client_list); list_free (rnh->zebra_static_route_list); + list_free (rnh->zebra_pseudowire_list); free_state (rnh->vrf_id, rnh->state, rnh->node); XFREE (MTYPE_RNH, rnh); } @@ -222,7 +224,8 @@ zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client, rnh_type_t type) } listnode_delete(rnh->client_list, client); if (list_isempty(rnh->client_list) && - list_isempty(rnh->zebra_static_route_list)) + list_isempty(rnh->zebra_static_route_list) && + list_isempty(rnh->zebra_pseudowire_list)) zebra_delete_rnh(rnh, type); } @@ -252,7 +255,8 @@ zebra_deregister_rnh_static_nh(vrf_id_t vrf_id, struct prefix *nh, listnode_delete(rnh->zebra_static_route_list, static_rn); if (list_isempty(rnh->client_list) && - list_isempty(rnh->zebra_static_route_list)) + list_isempty(rnh->zebra_static_route_list) && + list_isempty(rnh->zebra_pseudowire_list)) zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE); } @@ -301,6 +305,62 @@ zebra_deregister_rnh_static_nexthops (vrf_id_t vrf_id, struct nexthop *nexthop, } } +/* XXX move this utility function elsewhere? */ +static void +addr2hostprefix (int af, const union g_addr *addr, struct prefix *prefix) +{ + switch (af) + { + case AF_INET: + prefix->family = AF_INET; + prefix->prefixlen = IPV4_MAX_BITLEN; + prefix->u.prefix4 = addr->ipv4; + break; + case AF_INET6: + prefix->family = AF_INET6; + prefix->prefixlen = IPV6_MAX_BITLEN; + prefix->u.prefix6 = addr->ipv6; + break; + default: + zlog_warn ("%s: unknown address family %d", __func__, af); + break; + } +} + +void +zebra_register_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) +{ + struct prefix nh; + struct rnh *rnh; + + addr2hostprefix (pw->af, &pw->nexthop, &nh); + rnh = zebra_add_rnh (&nh, vrf_id, RNH_NEXTHOP_TYPE); + if (rnh && !listnode_lookup (rnh->zebra_pseudowire_list, pw)) + { + listnode_add (rnh->zebra_pseudowire_list, pw); + pw->rnh = rnh; + zebra_evaluate_rnh (vrf_id, pw->af, 1, RNH_NEXTHOP_TYPE, &nh); + } +} + +void +zebra_deregister_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) +{ + struct rnh *rnh; + + rnh = pw->rnh; + if (!rnh) + return; + + listnode_delete (rnh->zebra_pseudowire_list, pw); + pw->rnh = NULL; + + if (list_isempty (rnh->client_list) && + list_isempty (rnh->zebra_static_route_list) && + list_isempty (rnh->zebra_pseudowire_list)) + zebra_delete_rnh (rnh, RNH_NEXTHOP_TYPE); +} + /* Apply the NHT route-map for a client to the route (and nexthops) * resolving a NH. */ @@ -611,6 +671,16 @@ zebra_rnh_process_static_routes (vrf_id_t vrfid, int family, } } +static void +zebra_rnh_process_pseudowires (vrf_id_t vrfid, struct rnh *rnh) +{ + struct zebra_pw *pw; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (rnh->zebra_pseudowire_list, node, pw)) + zebra_pw_update (pw); +} + /* * See if a tracked nexthop entry has undergone any change, and if so, * take appropriate action; this involves notifying any clients and/or @@ -655,6 +725,9 @@ zebra_rnh_eval_nexthop_entry (vrf_id_t vrfid, int family, int force, /* Process static routes attached to this nexthop */ zebra_rnh_process_static_routes (vrfid, family, nrn, rnh, prn, rnh->state); + + /* Process pseudowires attached to this nexthop */ + zebra_rnh_process_pseudowires (vrfid, rnh); } } @@ -717,7 +790,10 @@ zebra_rnh_clear_nhc_flag (vrf_id_t vrfid, int family, rnh_type_t type, rib = zebra_rnh_resolve_entry (vrfid, family, type, nrn, rnh, &prn); if (rib) - UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + { + UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + UNSET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); + } } /* Evaluate all tracked entries (nexthops or routes for import into BGP) @@ -868,7 +944,8 @@ compare_state (struct rib *r1, struct rib *r2) if (r1->nexthop_num != r2->nexthop_num) return 1; - if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED)) + if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED) || + CHECK_FLAG(r1->status, RIB_ENTRY_LABELS_CHANGED)) return 1; return 0; @@ -1030,6 +1107,8 @@ print_rnh (struct route_node *rn, struct vty *vty) vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto), client->sock, rnh->filtered[client->proto] ? "(filtered)" : ""); if (!list_isempty(rnh->zebra_static_route_list)) - vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); + vty_out(vty, " zebra[static routes]%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); + if (!list_isempty(rnh->zebra_pseudowire_list)) + vty_out(vty, " zebra[pseudowires]"); vty_out(vty, "%s", VTY_NEWLINE); } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 4394fde4f..9da5138e9 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -42,6 +42,7 @@ struct rnh struct prefix resolved_route; struct list *client_list; struct list *zebra_static_route_list; /* static routes dependent on this NH */ + struct list *zebra_pseudowire_list; /* pseudowires dependent on this NH */ struct route_node *node; int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */ }; @@ -67,6 +68,8 @@ extern void zebra_register_rnh_static_nh(vrf_id_t, struct prefix *, struct route extern void zebra_deregister_rnh_static_nexthops (vrf_id_t, struct nexthop *nexthop, struct route_node *rn); extern void zebra_deregister_rnh_static_nh(vrf_id_t, struct prefix *, struct route_node *); +extern void zebra_register_rnh_pseudowire (vrf_id_t, struct zebra_pw *); +extern void zebra_deregister_rnh_pseudowire (vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, rnh_type_t type); extern void zebra_evaluate_rnh(vrf_id_t vrfid, int family, int force, rnh_type_t type, diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 6b3689105..1797ef080 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -246,6 +246,7 @@ zebra_vrf_delete (struct vrf *vrf) } zebra_mpls_close_tables (zvrf); + zebra_pw_exit (zvrf); for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp)) if_nbr_ipv6ll_to_ipv4ll_neigh_del_all (ifp); @@ -423,6 +424,7 @@ zebra_vrf_alloc (void) } zebra_mpls_init_tables (zvrf); + zebra_pw_init (zvrf); return zvrf; } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 96d631d64..56c98931f 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@ #define __ZEBRA_RIB_H__ #include <zebra/zebra_ns.h> +#include <zebra/zebra_pw.h> /* Routing table instance. */ struct zebra_vrf @@ -79,6 +80,10 @@ struct zebra_vrf /* MPLS label forwarding table */ struct hash *lsp_table; + /* Pseudowires. */ + struct zebra_pw_head pseudowires; + struct zebra_static_pw_head static_pseudowires; + /* MPLS processing flags */ u_int16_t mpls_flags; #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index a8bee3cf5..180ecd64a 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -932,6 +932,13 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib, break; } + if (nexthop->nh_label && nexthop->nh_label->num_labels) + { + json_object_string_add(json_nexthop, "labels", + mpls_label2str (nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, BUFSIZ)); + } + json_object_array_add(json_nexthops, json_nexthop); } diff --git a/zebra/zserv.c b/zebra/zserv.c index 9beae9232..fb02d60ba 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1013,6 +1013,28 @@ zsend_router_id_update (struct zserv *client, struct prefix *p, return zebra_server_send_message(client); } +/* + * Function used by Zebra to send a PW status update to LDP daemon + */ +int +zsend_pw_update (struct zserv *client, struct zebra_pw *pw) +{ + struct stream *s; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id); + stream_write (s, pw->ifname, IF_NAMESIZE); + stream_putl (s, pw->ifindex); + stream_putl (s, pw->status); + + /* Put length at the first point of the stream. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + return zebra_server_send_message (client); +} + /* Register zebra server interface information. Send current all interface and address information. */ static int @@ -1762,16 +1784,14 @@ zread_mpls_labels (int command, struct zserv *client, u_short length, { mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate, NULL, ifindex); - if (out_label != MPLS_IMP_NULL_LABEL) - mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex, - distance, out_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, NULL, ifindex); - if (out_label != MPLS_IMP_NULL_LABEL) - mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, - distance, out_label); + mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, + distance, out_label); } } /* Send response to a label manager connect request to client */ @@ -1933,6 +1953,103 @@ zread_label_manager_request (int cmd, struct zserv *client, vrf_id_t vrf_id) } } +static int +zread_pseudowire (int command, struct zserv *client, u_short length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct zebra_vrf *zvrf; + char ifname[IF_NAMESIZE]; + ifindex_t ifindex; + int type; + int af; + union g_addr nexthop; + uint32_t local_label; + uint32_t remote_label; + uint8_t flags; + union pw_protocol_fields data; + uint8_t protocol; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup (vrf_id); + if (!zvrf) + return -1; + + /* Get input stream. */ + s = client->ibuf; + + /* Get data. */ + stream_get (ifname, s, IF_NAMESIZE); + ifindex = stream_getl (s); + type = stream_getl (s); + af = stream_getl (s); + switch (af) + { + case AF_INET: + nexthop.ipv4.s_addr = stream_get_ipv4 (s); + break; + case AF_INET6: + stream_get (&nexthop.ipv6, s, 16); + break; + default: + return -1; + } + local_label = stream_getl (s); + remote_label = stream_getl (s); + flags = stream_getc (s); + stream_get (&data, s, sizeof(data)); + protocol = client->proto; + + pw = zebra_pw_find(zvrf, ifname); + switch (command) + { + case ZEBRA_PW_ADD: + if (pw) + { + zlog_warn ("%s: pseudowire %s already exists [%s]", __func__, ifname, + zserv_command_string (command)); + return -1; + } + + zebra_pw_add (zvrf, ifname, protocol, client); + break; + case ZEBRA_PW_DELETE: + if (!pw) + { + zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname, + zserv_command_string (command)); + return -1; + } + + zebra_pw_del (zvrf, pw); + break; + case ZEBRA_PW_SET: + case ZEBRA_PW_UNSET: + if (!pw) + { + zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname, + zserv_command_string (command)); + return -1; + } + + switch (command) + { + case ZEBRA_PW_SET: + pw->enabled = 1; + break; + case ZEBRA_PW_UNSET: + pw->enabled = 0; + break; + } + + zebra_pw_change (pw, ifindex, type, af, &nexthop, local_label, + remote_label, flags, &data); + break; + } + + return 0; +} + /* Cleanup registered nexthops (across VRFs) upon client disconnect. */ static void zebra_client_close_cleanup_rnh (struct zserv *client) @@ -1972,6 +2089,9 @@ zebra_client_close (struct zserv *client) /* Release Label Manager chunks */ release_daemon_chunks (client->proto, client->instance); + /* Remove pseudowires associated with this client */ + zebra_pw_client_close (client); + /* Close file descriptor. */ if (client->sock) { @@ -2269,6 +2389,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_RELEASE_LABEL_CHUNK: zread_label_manager_request (command, client, vrf_id); break; + case ZEBRA_PW_ADD: + case ZEBRA_PW_DELETE: + case ZEBRA_PW_SET: + case ZEBRA_PW_UNSET: + zread_pseudowire (command, client, length, vrf_id); + break; default: zlog_info ("Zebra received unknown command %d", command); break; diff --git a/zebra/zserv.h b/zebra/zserv.h index cd1948373..42e762caa 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -31,6 +31,8 @@ #include "zclient.h" #include "zebra/zebra_ns.h" +#include "zebra/zebra_pw.h" + /* Default port information. */ #define ZEBRA_VTY_PORT 2601 @@ -170,6 +172,7 @@ extern int zsend_interface_vrf_update (struct zserv *, struct interface *, vrf_id_t); extern int zsend_interface_link_params (struct zserv *, struct interface *); +extern int zsend_pw_update (struct zserv *, struct zebra_pw *); extern pid_t pid; |