summaryrefslogtreecommitdiffstats
path: root/ldpd/address.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldpd/address.c')
-rw-r--r--ldpd/address.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/ldpd/address.c b/ldpd/address.c
new file mode 100644
index 0000000..c3e2735
--- /dev/null
+++ b/ldpd/address.c
@@ -0,0 +1,425 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "ldpe.h"
+#include "lde.h"
+#include "log.h"
+#include "ldp_debug.h"
+
+static void send_address(struct nbr *, int, struct if_addr_head *,
+ unsigned int, 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,
+ unsigned int addr_count, int withdraw)
+{
+ struct ibuf *buf;
+ uint16_t msg_type;
+ uint8_t addr_size;
+ struct if_addr *if_addr;
+ uint16_t size;
+ unsigned int tlv_addr_count = 0;
+ int err = 0;
+
+ /* nothing to send */
+ if (LIST_EMPTY(addr_list))
+ return;
+
+ if (!withdraw)
+ msg_type = MSG_TYPE_ADDR;
+ else
+ msg_type = MSG_TYPE_ADDRWITHDRAW;
+
+ switch (af) {
+ case AF_INET:
+ addr_size = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ addr_size = sizeof(struct in6_addr);
+ break;
+ default:
+ fatalx("send_address: unknown af");
+ }
+
+ while (LIST_FIRST(addr_list) != NULL) {
+ /*
+ * Send as many addresses as possible - respect the session's
+ * negotiated maximum pdu length.
+ */
+ size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE;
+ if (size + addr_count * addr_size <= nbr->max_pdu_len)
+ tlv_addr_count = addr_count;
+ else
+ tlv_addr_count = (nbr->max_pdu_len - size) / addr_size;
+ size += tlv_addr_count * addr_size;
+ addr_count -= tlv_addr_count;
+
+ 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, size);
+ size -= LDP_MSG_SIZE;
+ err |= gen_address_list_tlv(buf, af, addr_list, tlv_addr_count);
+ (void)size;
+ if (err) {
+ address_list_clr(addr_list);
+ ibuf_free(buf);
+ return;
+ }
+
+ while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
+ 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);
+}
+
+void
+send_address_single(struct nbr *nbr, struct if_addr *if_addr, int withdraw)
+{
+ struct if_addr_head addr_list;
+
+ LIST_INIT(&addr_list);
+ address_list_add(&addr_list, if_addr);
+ send_address(nbr, if_addr->af, &addr_list, 1, withdraw);
+}
+
+void
+send_address_all(struct nbr *nbr, int af)
+{
+ struct if_addr_head addr_list;
+ struct if_addr *if_addr;
+ unsigned int addr_count = 0;
+
+ LIST_INIT(&addr_list);
+ LIST_FOREACH(if_addr, &global.addr_list, entry) {
+ if (if_addr->af != af)
+ continue;
+
+ address_list_add(&addr_list, if_addr);
+ addr_count++;
+ }
+
+ 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;
+ 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;
+
+ /* Address List TLV */
+ if (len < ADDR_LIST_SIZE) {
+ session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
+ return (-1);
+ }
+ memcpy(&alt, buf, sizeof(alt));
+ 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) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
+ return (-1);
+ }
+ switch (alt_family) {
+ case AF_IPV4:
+ if (!nbr->v4_enabled)
+ /* just ignore the message */
+ return (0);
+ break;
+ case AF_IPV6:
+ if (!nbr->v6_enabled)
+ /* just ignore the message */
+ return (0);
+ break;
+ default:
+ send_notification(nbr->tcp, S_UNSUP_ADDR, msg.id, msg.type);
+ return (-1);
+ }
+ alt_len -= sizeof(alt.family);
+ buf += sizeof(alt);
+ len -= sizeof(alt);
+
+ /* Process all received addresses */
+ while (alt_len > 0) {
+ switch (alt_family) {
+ case AF_IPV4:
+ if (alt_len < sizeof(struct in_addr)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ memset(&lde_addr, 0, sizeof(lde_addr));
+ lde_addr.af = AF_INET;
+ memcpy(&lde_addr.addr, buf, sizeof(struct in_addr));
+
+ buf += sizeof(struct in_addr);
+ len -= sizeof(struct in_addr);
+ alt_len -= sizeof(struct in_addr);
+ break;
+ case AF_IPV6:
+ if (alt_len < sizeof(struct in6_addr)) {
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+ msg.type);
+ return (-1);
+ }
+
+ memset(&lde_addr, 0, sizeof(lde_addr));
+ lde_addr.af = AF_INET6;
+ memcpy(&lde_addr.addr, buf, sizeof(struct in6_addr));
+
+ buf += sizeof(struct in6_addr);
+ len -= sizeof(struct in6_addr);
+ alt_len -= sizeof(struct in6_addr);
+ break;
+ default:
+ fatalx("recv_address: unknown af");
+ }
+
+ 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, int af, struct if_addr_head *addr_list,
+ unsigned int tlv_addr_count)
+{
+ struct address_list_tlv alt;
+ uint16_t addr_size;
+ struct if_addr *if_addr;
+ int err = 0;
+
+ memset(&alt, 0, sizeof(alt));
+ alt.type = htons(TLV_TYPE_ADDRLIST);
+
+ switch (af) {
+ case AF_INET:
+ alt.family = htons(AF_IPV4);
+ addr_size = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ alt.family = htons(AF_IPV6);
+ addr_size = sizeof(struct in6_addr);
+ break;
+ 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)
+ break;
+ }
+
+ 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)
+{
+ struct if_addr *new;
+
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ fatal(__func__);
+ *new = *if_addr;
+
+ LIST_INSERT_HEAD(addr_list, new, entry);
+}
+
+static void
+address_list_clr(struct if_addr_head *addr_list)
+{
+ struct if_addr *if_addr;
+
+ 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 %pI4, address %s", msg_name(msg_type),
+ &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 %pI4, mac %s", &nbr->id,
+ (mac) ? prefix_mac2str((struct ethaddr *)mac, buf, sizeof(buf)) :
+ "wildcard");
+}