RFC 4762 says that MAC address withdrawal messages can be used to
improve convergence time in VPLS networks. This patch makes ldpd send
MAC withdrawals whenever a non-pseudowire interface pertaining to a VPLS
goes down.

The processing of received MAC withdrawals will be implemented later (need
to figure out first a few things about RFC 4762 that are a bit unclear).
---
 address.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 kroute.c  |  3 +++
 l2vpn.c   | 29 +++++++++++++++++++++++-
 lde.h     |  1 +
 ldp.h     |  1 +
 ldpd.h    |  6 ++++-
 ldpe.c    | 24 +++++++++++++++-----
 ldpe.h    |  1 +
 8 files changed, 127 insertions(+), 15 deletions(-)

diff --git a/address.c b/address.c
index e891173..9609876 100644
--- a/address.c
+++ b/address.c
@@ -28,12 +28,14 @@
 
 static void     send_address(struct nbr *, int, struct if_addr_head *,
                    unsigned int, int);
-static int      gen_address_list_tlv(struct ibuf *, uint16_t, int,
-                   struct if_addr_head *, unsigned int);
+static int      gen_address_list_tlv(struct ibuf *, int, struct if_addr_head *,
+                   unsigned int);
+static int      gen_mac_list_tlv(struct ibuf *, uint8_t *);
 static void     address_list_add(struct if_addr_head *, struct if_addr *);
 static void     address_list_clr(struct if_addr_head *);
 static void     log_msg_address(int, uint16_t, struct nbr *, int,
                    union ldpd_addr *);
+static void     log_msg_mac_withdrawal(int, struct nbr *, uint8_t *);
 
 static void
 send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
@@ -87,8 +89,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head 
*addr_list,
                size -= LDP_HDR_SIZE;
                err |= gen_msg_hdr(buf, msg_type, size);
                size -= LDP_MSG_SIZE;
-               err |= gen_address_list_tlv(buf, size, af, addr_list,
-                   tlv_addr_count);
+               err |= gen_address_list_tlv(buf, af, addr_list, tlv_addr_count);
                if (err) {
                        address_list_clr(addr_list);
                        ibuf_free(buf);
@@ -139,6 +140,40 @@ send_address_all(struct nbr *nbr, int af)
        send_address(nbr, af, &addr_list, addr_count, 0);
 }
 
+void
+send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac)
+{
+       struct ibuf     *buf;
+       uint16_t         size;
+       int              err;
+
+       size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE + len_fec_tlv(fec) +
+           TLV_HDR_SIZE;
+       if (mac)
+               size += ETHER_ADDR_LEN;
+
+       if ((buf = ibuf_open(size)) == NULL)
+               fatal(__func__);
+
+       err = gen_ldp_hdr(buf, size);
+       size -= LDP_HDR_SIZE;
+       err |= gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size);
+       size -= LDP_MSG_SIZE;
+       err |= gen_address_list_tlv(buf, AF_INET, NULL, 0);
+       err |= gen_fec_tlv(buf, fec);
+       err |= gen_mac_list_tlv(buf, mac);
+       if (err) {
+               ibuf_free(buf);
+               return;
+       }
+
+       log_msg_mac_withdrawal(1, nbr, mac);
+
+       evbuf_enqueue(&nbr->tcp->wbuf, buf);
+
+       nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
 int
 recv_address(struct nbr *nbr, char *buf, uint16_t len)
 {
@@ -280,8 +315,8 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
 }
 
 static int
-gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
-    struct if_addr_head *addr_list, unsigned int tlv_addr_count)
+gen_address_list_tlv(struct ibuf *buf, int af, struct if_addr_head *addr_list,
+    unsigned int tlv_addr_count)
 {
        struct address_list_tlv  alt;
        uint16_t                 addr_size;
@@ -290,7 +325,6 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int 
af,
 
        memset(&alt, 0, sizeof(alt));
        alt.type = htons(TLV_TYPE_ADDRLIST);
-       alt.length = htons(size - TLV_HDR_SIZE);
 
        switch (af) {
        case AF_INET:
@@ -304,8 +338,12 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int 
af,
        default:
                fatalx("gen_address_list_tlv: unknown af");
        }
+       alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count);
 
        err |= ibuf_add(buf, &alt, sizeof(alt));
+       if (addr_list == NULL)
+               return (err);
+
        LIST_FOREACH(if_addr, addr_list, entry) {
                err |= ibuf_add(buf, &if_addr->addr, addr_size);
                if (--tlv_addr_count == 0)
@@ -315,6 +353,23 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int 
af,
        return (err);
 }
 
+static int
+gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac)
+{
+       struct tlv       tlv;
+       int              err;
+
+       memset(&tlv, 0, sizeof(tlv));
+       tlv.type = htons(TLV_TYPE_MAC_LIST);
+       if (mac)
+               tlv.length = htons(ETHER_ADDR_LEN);
+       err = ibuf_add(buf, &tlv, sizeof(tlv));
+       if (mac)
+               err |= ibuf_add(buf, mac, ETHER_ADDR_LEN);
+
+       return (err);
+}
+
 static void
 address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr)
 {
@@ -346,3 +401,11 @@ log_msg_address(int out, uint16_t msg_type, struct nbr 
*nbr, int af,
        log_debug("msg-%s: %s: lsr-id %s, address %s", (out) ? "out" : "in",
            msg_name(msg_type), inet_ntoa(nbr->id), log_addr(af, addr));
 }
+
+static void
+log_msg_mac_withdrawal(int out, struct nbr *nbr, uint8_t *mac)
+{
+       log_debug("msg-%s: mac withdrawal: lsr-id %s, mac %s",
+           (out) ? "out" : "in", inet_ntoa(nbr->id),
+           (mac) ? ether_ntoa((struct ether_addr *)mac) : "wildcard");
+}
diff --git a/kroute.c b/kroute.c
index b0e0106..7200e1c 100644
--- a/kroute.c
+++ b/kroute.c
@@ -25,6 +25,7 @@
 #include <sys/sysctl.h>
 #include <arpa/inet.h>
 #include <net/if_dl.h>
+#include <net/if_types.h>
 #include <net/route.h>
 #include <netmpls/mpls.h>
 #include <errno.h>
@@ -873,6 +874,8 @@ kif_update(unsigned short ifindex, int flags, struct 
if_data *ifd,
 
        kif->k.flags = flags;
        kif->k.link_state = ifd->ifi_link_state;
+       if (sdl)
+               memcpy(kif->k.mac, LLADDR(sdl), sizeof(kif->k.mac));
        kif->k.if_type = ifd->ifi_type;
        kif->k.baudrate = ifd->ifi_baudrate;
        kif->k.mtu = ifd->ifi_mtu;
diff --git a/l2vpn.c b/l2vpn.c
index 6da3560..04c3741 100644
--- a/l2vpn.c
+++ b/l2vpn.c
@@ -111,7 +111,7 @@ l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif)
        strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname));
        lif->ifindex = kif->ifindex;
        lif->flags = kif->flags;
-       lif->link_state = kif->link_state;
+       lif->linkstate = kif->link_state;
 
        return (lif);
 }
@@ -128,6 +128,33 @@ l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex)
        return (NULL);
 }
 
+void
+l2vpn_if_update(struct l2vpn_if *lif)
+{
+       struct l2vpn    *l2vpn = lif->l2vpn;
+       struct l2vpn_pw *pw;
+       struct map       fec;
+       struct nbr      *nbr;
+
+       if ((lif->flags & IFF_UP) && LINK_STATE_IS_UP(lif->linkstate))
+               return;
+
+       LIST_FOREACH(pw, &l2vpn->pw_list, entry) {
+               nbr = nbr_find_ldpid(pw->lsr_id.s_addr);
+               if (nbr == NULL)
+                       continue;
+
+               memset(&fec, 0, sizeof(fec));
+               fec.type = MAP_TYPE_PWID;
+               fec.fec.pwid.type = l2vpn->pw_type;
+               fec.fec.pwid.group_id = 0;
+               fec.flags |= F_MAP_PW_ID;
+               fec.fec.pwid.pwid = pw->pwid;
+
+               send_mac_withdrawal(nbr, &fec, lif->mac);
+       }
+}
+
 struct l2vpn_pw *
 l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif)
 {
diff --git a/lde.h b/lde.h
index 9d560e1..c297133 100644
--- a/lde.h
+++ b/lde.h
@@ -198,6 +198,7 @@ void                 l2vpn_init(struct l2vpn *);
 void            l2vpn_exit(struct l2vpn *);
 struct l2vpn_if        *l2vpn_if_new(struct l2vpn *, struct kif *);
 struct l2vpn_if        *l2vpn_if_find(struct l2vpn *, unsigned int);
+void            l2vpn_if_update(struct l2vpn_if *);
 struct l2vpn_pw        *l2vpn_pw_new(struct l2vpn *, struct kif *);
 struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, unsigned int);
 void            l2vpn_pw_init(struct l2vpn_pw *);
diff --git a/ldp.h b/ldp.h
index d5751df..d409062 100644
--- a/ldp.h
+++ b/ldp.h
@@ -95,6 +95,7 @@
 #define TLV_TYPE_FRSESSION     0x0502
 #define TLV_TYPE_LABELREQUEST  0x0600
 /* RFC 4447 */
+#define TLV_TYPE_MAC_LIST      0x8404
 #define TLV_TYPE_PW_STATUS     0x896A
 #define TLV_TYPE_PW_IF_PARAM   0x096B
 #define TLV_TYPE_PW_GROUP_ID   0x096C
diff --git a/ldpd.h b/ldpd.h
index 1436f76..117fea1 100644
--- a/ldpd.h
+++ b/ldpd.h
@@ -26,8 +26,10 @@
 #include <sys/socket.h>
 #include <sys/queue.h>
 #include <sys/tree.h>
+#include <net/if_arp.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#include <netinet/if_ether.h>
 #include <event.h>
 #include <imsg.h>
 
@@ -327,7 +329,8 @@ struct l2vpn_if {
        char                     ifname[IF_NAMESIZE];
        unsigned int             ifindex;
        uint16_t                 flags;
-       uint8_t                  link_state;
+       uint8_t                  linkstate;
+       uint8_t                  mac[ETHER_ADDR_LEN];
 };
 
 struct l2vpn_pw {
@@ -474,6 +477,7 @@ struct kif {
        unsigned short           ifindex;
        int                      flags;
        uint8_t                  link_state;
+       uint8_t                  mac[ETHER_ADDR_LEN];
        int                      mtu;
        unsigned int             rdomain;
        uint8_t                  if_type;
diff --git a/ldpe.c b/ldpe.c
index e2c6f72..ec7c8f0 100644
--- a/ldpe.c
+++ b/ldpe.c
@@ -221,8 +221,8 @@ ldpe_dispatch_main(int fd, short event, void *bula)
        struct iface            *niface;
        struct tnbr             *ntnbr;
        struct nbr_params       *nnbrp;
-       static struct l2vpn     *nl2vpn;
-       struct l2vpn_if         *nlif;
+       static struct l2vpn     *l2vpn, *nl2vpn;
+       struct l2vpn_if         *lif = NULL, *nlif;
        struct l2vpn_pw         *npw;
        struct imsg              imsg;
        struct imsgev           *iev = bula;
@@ -265,12 +265,24 @@ ldpe_dispatch_main(int fd, short event, void *bula)
                        kif = imsg.data;
 
                        iface = if_lookup(leconf, kif->ifindex);
-                       if (!iface)
+                       if (iface) {
+                               iface->flags = kif->flags;
+                               iface->linkstate = kif->link_state;
+                               if_update(iface, AF_UNSPEC);
                                break;
+                       }
 
-                       iface->flags = kif->flags;
-                       iface->linkstate = kif->link_state;
-                       if_update(iface, AF_UNSPEC);
+                       LIST_FOREACH(l2vpn, &leconf->l2vpn_list, entry) {
+                               lif = l2vpn_if_find(l2vpn, kif->ifindex);
+                               if (lif) {
+                                       lif->flags = kif->flags;
+                                       lif->linkstate = kif->link_state;
+                                       memcpy(lif->mac, kif->mac,
+                                           sizeof(lif->mac));
+                                       l2vpn_if_update(lif);
+                                       break;
+                               }
+                       }
                        break;
                case IMSG_NEWADDR:
                        if (imsg.hdr.len != IMSG_HEADER_SIZE +
diff --git a/ldpe.h b/ldpe.h
index e51d054..9a1d11a 100644
--- a/ldpe.h
+++ b/ldpe.h
@@ -173,6 +173,7 @@ int  gen_status_tlv(struct ibuf *, uint32_t, uint32_t, 
uint16_t);
 /* address.c */
 void    send_address_single(struct nbr *, struct if_addr *, int);
 void    send_address_all(struct nbr *, int);
+void    send_mac_withdrawal(struct nbr *, struct map *, uint8_t *);
 int     recv_address(struct nbr *, char *, uint16_t);
 
 /* labelmapping.c */
-- 
1.9.1

Reply via email to