summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ldpd/labelmapping.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/ldpd/labelmapping.c')
-rw-r--r--usr.sbin/ldpd/labelmapping.c579
1 files changed, 579 insertions, 0 deletions
diff --git a/usr.sbin/ldpd/labelmapping.c b/usr.sbin/ldpd/labelmapping.c
new file mode 100644
index 00000000000..aac097161b7
--- /dev/null
+++ b/usr.sbin/ldpd/labelmapping.c
@@ -0,0 +1,579 @@
+/* $OpenBSD: labelmapping.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */
+
+/*
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldpd.h"
+#include "ldp.h"
+#include "log.h"
+#include "ldpe.h"
+
+void gen_fec_tlv(struct buf *, u_int32_t, u_int8_t);
+void gen_label_tlv(struct buf *, u_int32_t);
+
+u_int32_t tlv_decode_label(struct label_tlv *);
+u_int32_t decode_fec_elm(char *);
+u_int8_t decode_fec_len_elm(char *);
+int validate_fec_elm(char *);
+
+/* Label Mapping Message */
+int
+send_labelmapping(struct nbr *nbr)
+{
+ struct buf *buf;
+ struct mapping_entry *me;
+ struct ldp_hdr *ldp_hdr;
+ u_int16_t tlv_size, size;
+
+ if (nbr->iface->passive)
+ return (0);
+
+ log_debug("send_labelmapping: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if ((buf = buf_open(LDP_MAX_LEN)) == NULL)
+ fatal("send_labelmapping");
+
+ /* real size will be set up later */
+ gen_ldp_hdr(buf, nbr->iface, 0);
+
+ size = LDP_HDR_SIZE - TLV_HDR_LEN;
+
+ TAILQ_FOREACH(me, &nbr->mapping_list, entry) {
+ tlv_size = BASIC_LABEL_MAP_LEN + me->prefixlen/8;
+ size += tlv_size;
+
+ gen_msg_tlv(buf, MSG_TYPE_LABELMAPPING, tlv_size);
+ gen_fec_tlv(buf, me->prefix, me->prefixlen);
+ gen_label_tlv(buf, me->label);
+ }
+
+ /* XXX: should we remove them first? */
+ nbr_mapping_list_clr(nbr, &nbr->mapping_list);
+
+ ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr));
+ ldp_hdr->length = htons(size);
+
+ bufferevent_write(nbr->bev, buf->buf, buf->wpos);
+ buf_free(buf);
+
+ return (0);
+}
+
+int
+recv_labelmapping(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct ldp_msg *lm;
+ struct fec_tlv *ft;
+ struct label_tlv *lt;
+ struct map map;
+ u_int32_t messageid;
+ int feclen;
+
+ log_debug("recv_labelmapping: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->state != NBR_STA_OPER)
+ return (-1);
+
+ bzero(&map, sizeof(map));
+ lm = (struct ldp_msg *)buf;
+
+ if ((len - TLV_HDR_LEN) < ntohs(lm->length)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ messageid = lm->msgid;
+
+ buf += sizeof(struct ldp_msg);
+ len -= sizeof(struct ldp_msg);
+
+ ft = (struct fec_tlv *)buf;
+ lt = (struct label_tlv *)(buf + TLV_HDR_LEN + ntohs(ft->length));
+
+ if (len < (sizeof(*ft) + LABEL_TLV_LEN)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ feclen = ntohs(ft->length);
+ if (len - TLV_HDR_LEN < feclen) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ map.label = tlv_decode_label(lt);
+ if (map.label == NO_LABEL) {
+ log_debug("recv_labelmapping: packet malformed from "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ buf += sizeof(struct fec_tlv);
+ len -= sizeof(struct fec_tlv);
+
+ while (feclen >= FEC_ELM_MIN_LEN) {
+ if (validate_fec_elm(buf) < 0) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ map.prefix = decode_fec_elm(buf);
+ map.prefixlen = decode_fec_len_elm(buf);
+ map.prefix &= prefixlen2mask(map.prefixlen);
+
+ ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING, nbr->peerid, 0, &map,
+ sizeof(map));
+
+ buf += FEC_ELM_MIN_LEN + map.prefixlen/8;
+ feclen -= (FEC_ELM_MIN_LEN + map.prefixlen/8);
+ }
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ return (ntohs(lm->length));
+}
+
+/* Label Request Message */
+int
+send_labelrequest(struct nbr *nbr)
+{
+ struct buf *buf;
+ struct mapping_entry *me;
+ struct ldp_hdr *ldp_hdr;
+ u_int16_t tlv_size, size;
+
+ if (nbr->iface->passive)
+ return (0);
+
+ log_debug("send_labelrequest: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if ((buf = buf_open(LDP_MAX_LEN)) == NULL)
+ fatal("send_labelrequest");
+
+ /* real size will be set up later */
+ gen_ldp_hdr(buf, nbr->iface, 0);
+
+ size = LDP_HDR_SIZE - TLV_HDR_LEN;
+
+ TAILQ_FOREACH(me, &nbr->request_list, entry) {
+ tlv_size = me->prefixlen/8;
+ size += tlv_size;
+
+ gen_msg_tlv(buf, MSG_TYPE_LABELREQUEST, tlv_size);
+ gen_fec_tlv(buf, me->prefix, me->prefixlen);
+ }
+
+ /* XXX: should we remove them first? */
+ nbr_mapping_list_clr(nbr, &nbr->request_list);
+
+ ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr));
+ ldp_hdr->length = htons(size);
+
+ bufferevent_write(nbr->bev, buf->buf, buf->wpos);
+ buf_free(buf);
+
+ return (0);
+}
+
+int
+recv_labelrequest(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct ldp_msg *lr;
+ struct fec_tlv *ft;
+ struct map map;
+ u_int32_t messageid;
+ int feclen;
+
+ log_debug("recv_labelrequest: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->state != NBR_STA_OPER)
+ return (-1);
+
+ bzero(&map, sizeof(map));
+ lr = (struct ldp_msg *)buf;
+
+ if ((len - TLV_HDR_LEN) < ntohs(lr->length)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ messageid = lr->msgid;
+
+ buf += sizeof(struct ldp_msg);
+ len -= sizeof(struct ldp_msg);
+
+ ft = (struct fec_tlv *)buf;
+
+ if (len < sizeof(*ft) ||
+ (len - TLV_HDR_LEN) < ntohs(ft->length)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ feclen = ntohs(ft->length);
+
+ buf += sizeof(struct fec_tlv);
+ len -= sizeof(struct fec_tlv);
+
+ while (feclen >= FEC_ELM_MIN_LEN) {
+ if (validate_fec_elm(buf) < 0) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ map.prefix = decode_fec_elm(buf);
+ map.prefixlen = decode_fec_len_elm(buf);
+ map.prefix &= prefixlen2mask(map.prefixlen);
+ map.messageid = messageid;
+
+ ldpe_imsg_compose_lde(IMSG_LABEL_REQUEST, nbr->peerid, 0, &map,
+ sizeof(map));
+
+ buf += FEC_ELM_MIN_LEN + map.prefixlen/8;
+ feclen -= (FEC_ELM_MIN_LEN + map.prefixlen/8);
+ }
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ return (0);
+}
+
+/* Label Withdraw Message */
+int
+send_labelwithdraw(struct nbr *nbr)
+{
+ struct buf *buf;
+ struct mapping_entry *me;
+ struct ldp_hdr *ldp_hdr;
+ u_int16_t tlv_size, size;
+
+ if (nbr->iface->passive)
+ return (0);
+
+ log_debug("send_labelwithdraw: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if ((buf = buf_open(LDP_MAX_LEN)) == NULL)
+ fatal("send_labelwithdraw");
+
+ /* real size will be set up later */
+ gen_ldp_hdr(buf, nbr->iface, 0);
+
+ size = LDP_HDR_SIZE - TLV_HDR_LEN;
+
+ TAILQ_FOREACH(me, &nbr->withdraw_list, entry) {
+ if (me->label == NO_LABEL)
+ tlv_size = me->prefixlen/8;
+ else
+ tlv_size = BASIC_LABEL_MAP_LEN + me->prefixlen/8;
+
+ size += tlv_size;
+
+ gen_msg_tlv(buf, MSG_TYPE_LABELWITHDRAW, tlv_size);
+ gen_fec_tlv(buf, me->prefix, me->prefixlen);
+
+ if (me->label != NO_LABEL)
+ gen_label_tlv(buf, me->label);
+ }
+
+ /* XXX: should we remove them first? */
+ nbr_mapping_list_clr(nbr, &nbr->withdraw_list);
+
+ ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr));
+ ldp_hdr->length = htons(size);
+
+ bufferevent_write(nbr->bev, buf->buf, buf->wpos);
+
+ buf_free(buf);
+
+ return (0);
+}
+
+int
+recv_labelwithdraw(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct ldp_msg *lw;
+ u_int32_t messageid;
+
+ log_debug("recv_labelwithdraw: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->state != NBR_STA_OPER)
+ return (-1);
+
+ lw = (struct ldp_msg *)buf;
+
+ if ((len - TLV_HDR_LEN) < ntohs(lw->length)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ messageid = lw->msgid;
+
+ buf += sizeof(struct ldp_msg);
+ len -= sizeof(struct ldp_msg);
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ return (0);
+}
+
+/* Label Release Message */
+int
+send_labelrelease(struct nbr *nbr)
+{
+ struct buf *buf;
+ struct mapping_entry *me;
+ struct ldp_hdr *ldp_hdr;
+ u_int16_t tlv_size, size;
+
+ if (nbr->iface->passive)
+ return (0);
+
+ log_debug("send_labelrelease: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if ((buf = buf_open(LDP_MAX_LEN)) == NULL)
+ fatal("send_labelrelease");
+
+ /* real size will be set up later */
+ gen_ldp_hdr(buf, nbr->iface, 0);
+
+ size = LDP_HDR_SIZE - TLV_HDR_LEN;
+
+ TAILQ_FOREACH(me, &nbr->release_list, entry) {
+ if (me->label == NO_LABEL)
+ tlv_size = me->prefixlen/8;
+ else
+ tlv_size = BASIC_LABEL_MAP_LEN + me->prefixlen/8;
+
+ size += tlv_size;
+
+ gen_msg_tlv(buf, MSG_TYPE_LABELRELEASE, tlv_size);
+ gen_fec_tlv(buf, me->prefix, me->prefixlen);
+
+ if (me->label != NO_LABEL)
+ gen_label_tlv(buf, me->label);
+ }
+
+ /* XXX: should we remove them first? */
+ nbr_mapping_list_clr(nbr, &nbr->release_list);
+
+ ldp_hdr = buf_seek(buf, 0, sizeof(struct ldp_hdr));
+ ldp_hdr->length = htons(size);
+
+ bufferevent_write(nbr->bev, buf->buf, buf->wpos);
+ buf_free(buf);
+
+ return (0);
+}
+
+int
+recv_labelrelease(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct ldp_msg *lr;
+ u_int32_t messageid;
+
+ log_debug("recv_labelrelease: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->state != NBR_STA_OPER)
+ return (-1);
+
+ lr = (struct ldp_msg *)buf;
+
+ if ((len - TLV_HDR_LEN) < ntohs(lr->length)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ messageid = lr->msgid;
+
+ buf += sizeof(struct ldp_msg);
+ len -= sizeof(struct ldp_msg);
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ return (0);
+}
+
+/* Label Abort Req Message */
+int
+send_labelabortreq(struct nbr *nbr)
+{
+ struct buf *buf;
+ u_int16_t size;
+
+ if (nbr->iface->passive)
+ return (0);
+
+ log_debug("send_labelabortreq: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if ((buf = buf_open(LDP_MAX_LEN)) == NULL)
+ fatal("send_labelabortreq");
+
+ size = LDP_HDR_SIZE + sizeof(struct ldp_msg);
+
+ gen_ldp_hdr(buf, nbr->iface, size);
+
+ size -= LDP_HDR_SIZE;
+
+ gen_msg_tlv(buf, MSG_TYPE_LABELABORTREQ, size);
+
+ bufferevent_write(nbr->bev, buf->buf, buf->wpos);
+
+ buf_free(buf);
+
+ return (0);
+}
+
+int
+recv_labelabortreq(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct ldp_msg *la;
+ u_int32_t messageid;
+
+ log_debug("recv_labelabortreq: neighbor ID %s", inet_ntoa(nbr->id));
+
+ if (nbr->state != NBR_STA_OPER)
+ return (-1);
+
+ la = (struct ldp_msg *)buf;
+
+ if ((len - TLV_HDR_LEN) < ntohs(la->length)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ messageid = la->msgid;
+
+ buf += sizeof(struct ldp_msg);
+ len -= sizeof(struct ldp_msg);
+
+ nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+ return (0);
+}
+
+/* Other TLV related functions */
+void
+gen_fec_tlv(struct buf *buf, u_int32_t prefix, u_int8_t prefixlen)
+{
+ struct fec_tlv ft;
+ u_int8_t type;
+ u_int16_t family;
+ u_int8_t len;
+ u_int32_t addr;
+
+ ft.type = htons(TLV_TYPE_FEC);
+ ft.length = htons(sizeof(ft) + (int)(prefixlen/8));
+
+ buf_add(buf, &ft, sizeof(ft));
+
+ if (prefixlen == 32)
+ type = FEC_ADDRESS;
+ else
+ type = FEC_PREFIX;
+ family = htons(FEC_IPV4);
+ len = prefixlen;
+ addr = prefix;
+
+ buf_add(buf, &type, sizeof(type));
+ buf_add(buf, &family, sizeof(family));
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &addr, (int)(prefixlen/8));
+}
+
+void
+gen_label_tlv(struct buf *buf, u_int32_t label)
+{
+ struct label_tlv lt;
+
+ lt.type = htons(TLV_TYPE_GENERICLABEL);
+ lt.length = htons(sizeof(label));
+ lt.label = htonl(label);
+
+ buf_add(buf, &lt, sizeof(lt));
+}
+
+u_int32_t
+tlv_decode_label(struct label_tlv *lt)
+{
+ if (lt->type != htons(TLV_TYPE_GENERICLABEL)) {
+ /* XXX: send notification */
+ return (NO_LABEL);
+ }
+
+ if (ntohs(lt->length) != sizeof(lt->label)) {
+ /* XXX: send notification */
+ return (NO_LABEL);
+ }
+
+ return (ntohl(lt->label));
+}
+
+int
+validate_fec_elm(char *buf)
+{
+ u_int16_t *family;
+
+ if (*buf != FEC_WILDCARD && *buf != FEC_PREFIX && *buf !=
+ FEC_ADDRESS) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ buf += sizeof(u_int8_t);
+ family = (u_int16_t *)buf;
+
+ if (*family != htons(FEC_IPV4)) {
+ /* XXX: send notification */
+ return (-1);
+ }
+
+ return (0);
+}
+
+u_int32_t
+decode_fec_elm(char *buf)
+{
+ struct fec_elm *fe = (struct fec_elm *)buf;
+
+ return (fe->addr);
+}
+
+u_int8_t
+decode_fec_len_elm(char *buf)
+{
+ /* Skip type and family */
+ buf += sizeof(u_int8_t);
+ buf += sizeof(u_int16_t);
+
+ return (*buf);
+}