]> git.proxmox.com Git - mirror_frr.git/blobdiff - ldpd/address.c
Merge pull request #3069 from donaldsharp/bgp_nexthop_address
[mirror_frr.git] / ldpd / address.c
index 1c4c116f216f7ecd6d1cad73a473620bd88dec35..9c1564a31f88d66f8a033f9ce771e9e956924286 100644 (file)
 
 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,
@@ -83,8 +87,8 @@ 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);
+               (void)size;
                if (err) {
                        address_list_clr(addr_list);
                        ibuf_free(buf);
@@ -92,17 +96,28 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
                }
 
                while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
-                       debug_msg_send("%s: lsr-id %s address %s",
-                           msg_name(msg_type), inet_ntoa(nbr->id),
-                           log_addr(af, &if_addr->addr));
+                       log_msg_address(1, msg_type, nbr, af, &if_addr->addr);
 
                        LIST_REMOVE(if_addr, entry);
+                       assert(if_addr != LIST_FIRST(addr_list));
                        free(if_addr);
                        if (--tlv_addr_count == 0)
                                break;
                }
 
                evbuf_enqueue(&nbr->tcp->wbuf, buf);
+
+               /* no errors - update per neighbor message counters */
+               switch (msg_type) {
+               case MSG_TYPE_ADDR:
+                       nbr->stats.addr_sent++;
+                       break;
+               case MSG_TYPE_ADDRWITHDRAW:
+                       nbr->stats.addrwdraw_sent++;
+                       break;
+               default:
+                       break;
+               }
        }
 
        nbr_fsm(nbr, NBR_EVT_PDU_SENT);
@@ -137,16 +152,62 @@ 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 += ETH_ALEN;
+
+       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);
+       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)
 {
        struct ldp_msg          msg;
        uint16_t                msg_type;
-       struct address_list_tlv alt;
        enum imsg_type          type;
+       struct address_list_tlv alt;
+       uint16_t                alt_len;
+       uint16_t                alt_family;
        struct lde_addr         lde_addr;
 
        memcpy(&msg, buf, sizeof(msg));
+       msg_type = ntohs(msg.type);
+       switch (msg_type) {
+       case MSG_TYPE_ADDR:
+               type = IMSG_ADDRESS_ADD;
+               break;
+       case MSG_TYPE_ADDRWITHDRAW:
+               type = IMSG_ADDRESS_DEL;
+               break;
+       default:
+               fatalx("recv_address: unexpected msg type");
+       }
        buf += LDP_MSG_SIZE;
        len -= LDP_MSG_SIZE;
 
@@ -155,17 +216,18 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
                session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
                return (-1);
        }
-
        memcpy(&alt, buf, sizeof(alt));
-       if (ntohs(alt.length) != len - TLV_HDR_SIZE) {
+       alt_len = ntohs(alt.length);
+       alt_family = ntohs(alt.family);
+       if (alt_len > len - TLV_HDR_SIZE) {
                session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
                return (-1);
        }
        if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) {
-               session_shutdown(nbr, S_UNKNOWN_TLV, msg.id, msg.type);
+               send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
                return (-1);
        }
-       switch (ntohs(alt.family)) {
+       switch (alt_family) {
        case AF_IPV4:
                if (!nbr->v4_enabled)
                        /* just ignore the message */
@@ -177,22 +239,18 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
                        return (0);
                break;
        default:
-               send_notification_nbr(nbr, S_UNSUP_ADDR, msg.id, msg.type);
+               send_notification(nbr->tcp, S_UNSUP_ADDR, msg.id, msg.type);
                return (-1);
        }
+       alt_len -= sizeof(alt.family);
        buf += sizeof(alt);
        len -= sizeof(alt);
 
-       msg_type = ntohs(msg.type);
-       if (msg_type == MSG_TYPE_ADDR)
-               type = IMSG_ADDRESS_ADD;
-       else
-               type = IMSG_ADDRESS_DEL;
-
-       while (len > 0) {
-               switch (ntohs(alt.family)) {
+       /* Process all received addresses */
+       while (alt_len > 0) {
+               switch (alt_family) {
                case AF_IPV4:
-                       if (len < sizeof(struct in_addr)) {
+                       if (alt_len < sizeof(struct in_addr)) {
                                session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
                                    msg.type);
                                return (-1);
@@ -204,9 +262,10 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
 
                        buf += sizeof(struct in_addr);
                        len -= sizeof(struct in_addr);
+                       alt_len -= sizeof(struct in_addr);
                        break;
                case AF_IPV6:
-                       if (len < sizeof(struct in6_addr)) {
+                       if (alt_len < sizeof(struct in6_addr)) {
                                session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
                                    msg.type);
                                return (-1);
@@ -218,24 +277,57 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len)
 
                        buf += sizeof(struct in6_addr);
                        len -= sizeof(struct in6_addr);
+                       alt_len -= sizeof(struct in6_addr);
                        break;
                default:
                        fatalx("recv_address: unknown af");
                }
 
-               debug_msg_recv("%s: lsr-id %s address %s", msg_name(msg_type),
-                   inet_ntoa(nbr->id), log_addr(lde_addr.af, &lde_addr.addr));
+               log_msg_address(0, msg_type, nbr, lde_addr.af, &lde_addr.addr);
 
                ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr,
                    sizeof(lde_addr));
        }
 
+       /* Optional Parameters */
+       while (len > 0) {
+               struct tlv      tlv;
+               uint16_t        tlv_type;
+               uint16_t        tlv_len;
+
+               if (len < sizeof(tlv)) {
+                       session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+                       return (-1);
+               }
+
+               memcpy(&tlv, buf, TLV_HDR_SIZE);
+               tlv_type = ntohs(tlv.type);
+               tlv_len = ntohs(tlv.length);
+               if (tlv_len + TLV_HDR_SIZE > len) {
+                       session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+                       return (-1);
+               }
+               buf += TLV_HDR_SIZE;
+               len -= TLV_HDR_SIZE;
+
+               switch (tlv_type) {
+               default:
+                       if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+                               send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
+                                   msg.id, msg.type, tlv_type, tlv_len, buf);
+                       /* ignore unknown tlv */
+                       break;
+               }
+               buf += tlv_len;
+               len -= tlv_len;
+       }
+
        return (0);
 }
 
 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;
@@ -243,8 +335,7 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
        int                      err = 0;
 
        memset(&alt, 0, sizeof(alt));
-       alt.type = TLV_TYPE_ADDRLIST;
-       alt.length = htons(size - TLV_HDR_SIZE);
+       alt.type = htons(TLV_TYPE_ADDRLIST);
 
        switch (af) {
        case AF_INET:
@@ -258,8 +349,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)
@@ -269,6 +364,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(ETH_ALEN);
+       err = ibuf_add(buf, &tlv, sizeof(tlv));
+       if (mac)
+               err |= ibuf_add(buf, mac, ETH_ALEN);
+
+       return (err);
+}
+
 static void
 address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr)
 {
@@ -289,6 +401,25 @@ address_list_clr(struct if_addr_head *addr_list)
 
        while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
                LIST_REMOVE(if_addr, entry);
+               assert(if_addr != LIST_FIRST(addr_list));
                free(if_addr);
        }
 }
+
+static void
+log_msg_address(int out, uint16_t msg_type, struct nbr *nbr, int af,
+    union ldpd_addr *addr)
+{
+       debug_msg(out, "%s: lsr-id %s, address %s", 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)
+{
+       char buf[ETHER_ADDR_STRLEN];
+
+       debug_msg(out, "mac withdrawal: lsr-id %s, mac %s", inet_ntoa(nbr->id),
+           (mac) ? prefix_mac2str((struct ethaddr *)mac, buf, sizeof(buf)) :
+           "wildcard");
+}