root/usr.sbin/bgpd/bgpd_imsg.c
/*      $OpenBSD: bgpd_imsg.c,v 1.4 2026/03/19 15:36:44 claudio Exp $   */
/*
 * Copyright (c) 2026 Claudio Jeker <claudio@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 <errno.h>
#include <stdlib.h>
#include <string.h>

#include "bgpd.h"
#include "rde.h"

int
imsg_send_filterset(struct imsgbuf *imsgbuf, struct filter_set_head *set)
{
        struct filter_set       *s;
        struct ibuf             *msg;
        int                      nsets = 0;

        msg = imsg_create(imsgbuf, IMSG_FILTER_SET, 0, 0, 0);
        if (msg == NULL)
                return -1;

        TAILQ_FOREACH(s, set, entry)
                nsets++;
        if (ibuf_add_n16(msg, nsets) == -1)
                goto fail;

        TAILQ_FOREACH(s, set, entry) {
                if (ibuf_add_n32(msg, s->type) == -1)
                        goto fail;

                switch (s->type) {
                case ACTION_SET_PREPEND_SELF:
                case ACTION_SET_PREPEND_PEER:
                        if (ibuf_add_n8(msg, s->action.prepend) == -1)
                                goto fail;
                        break;
                case ACTION_SET_AS_OVERRIDE:
                        break;
                case ACTION_SET_LOCALPREF:
                case ACTION_SET_MED:
                case ACTION_SET_WEIGHT:
                        if (ibuf_add_n32(msg, s->action.metric) == -1)
                                goto fail;
                        break;
                case ACTION_SET_RELATIVE_LOCALPREF:
                case ACTION_SET_RELATIVE_MED:
                case ACTION_SET_RELATIVE_WEIGHT:
                        if (ibuf_add(msg, &s->action.relative,
                            sizeof(s->action.relative)) == -1)
                                goto fail;
                        break;
                case ACTION_SET_NEXTHOP:
                        if (ibuf_add(msg, &s->action.nexthop,
                            sizeof(s->action.nexthop)) == -1)
                                goto fail;
                        break;
                case ACTION_SET_NEXTHOP_BLACKHOLE:
                case ACTION_SET_NEXTHOP_REJECT:
                case ACTION_SET_NEXTHOP_NOMODIFY:
                case ACTION_SET_NEXTHOP_SELF:
                        break;
                case ACTION_DEL_COMMUNITY:
                case ACTION_SET_COMMUNITY:
                        if (ibuf_add(msg, &s->action.community,
                            sizeof(s->action.community)) == -1)
                                goto fail;
                        break;
                case ACTION_PFTABLE:
                        if (ibuf_add_strbuf(msg, s->action.pftable,
                            sizeof(s->action.pftable)) == -1)
                                goto fail;
                        break;
                case ACTION_RTLABEL:
                        if (ibuf_add_strbuf(msg, s->action.rtlabel,
                            sizeof(s->action.rtlabel)) == -1)
                                goto fail;
                        break;
                case ACTION_SET_ORIGIN:
                        if (ibuf_add_n8(msg, s->action.origin) == -1)
                                goto fail;
                        break;
                }
        }

        imsg_close(imsgbuf, msg);
        return 0;

 fail:
        ibuf_free(msg);
        return -1;
}

int
ibuf_recv_filterset_count(struct ibuf *ibuf, uint16_t *count)
{
        return ibuf_get_n16(ibuf, count);
}

int
ibuf_recv_one_filterset(struct ibuf *ibuf, struct filter_set *set)
{
        uint32_t type;

        memset(set, 0, sizeof(*set));

        if (ibuf_get_n32(ibuf, &type) == -1)
                return -1;
        set->type = type;

        switch (set->type) {
        case ACTION_SET_PREPEND_SELF:
        case ACTION_SET_PREPEND_PEER:
                if (ibuf_get_n8(ibuf, &set->action.prepend) == -1)
                        return -1;
                break;
        case ACTION_SET_AS_OVERRIDE:
                break;
        case ACTION_SET_LOCALPREF:
        case ACTION_SET_MED:
        case ACTION_SET_WEIGHT:
                if (ibuf_get_n32(ibuf, &set->action.metric) == -1)
                        return -1;
                break;
        case ACTION_SET_RELATIVE_LOCALPREF:
        case ACTION_SET_RELATIVE_MED:
        case ACTION_SET_RELATIVE_WEIGHT:
                if (ibuf_get(ibuf, &set->action.relative,
                    sizeof(set->action.relative)) == -1)
                        return -1;
                break;
        case ACTION_SET_NEXTHOP:
                if (ibuf_get(ibuf, &set->action.nexthop,
                    sizeof(set->action.nexthop)) == -1)
                        return -1;
                break;
        case ACTION_SET_NEXTHOP_BLACKHOLE:
        case ACTION_SET_NEXTHOP_REJECT:
        case ACTION_SET_NEXTHOP_NOMODIFY:
        case ACTION_SET_NEXTHOP_SELF:
                break;
        case ACTION_DEL_COMMUNITY:
        case ACTION_SET_COMMUNITY:
                if (ibuf_get(ibuf, &set->action.community,
                    sizeof(set->action.community)) == -1)
                        return -1;
                break;
        case ACTION_PFTABLE:
                if (ibuf_get_strbuf(ibuf, set->action.pftable,
                    sizeof(set->action.pftable)) == -1)
                        return -1;
                break;
        case ACTION_RTLABEL:
                if (ibuf_get_strbuf(ibuf, set->action.rtlabel,
                    sizeof(set->action.rtlabel)) == -1)
                        return -1;
                break;
        case ACTION_SET_ORIGIN:
                if (ibuf_get_n8(ibuf, &set->action.origin) == -1)
                        return -1;
                break;
        }
        return 0;
}

int
imsg_check_filterset(struct imsg *imsg)
{
        struct ibuf ibuf;
        uint16_t count, i;

        if (imsg_get_ibuf(imsg, &ibuf) == -1)
                return -1;
        if (ibuf_recv_filterset_count(&ibuf, &count) == -1)
                return -1;
        for (i = 0; i < count; i++) {
                struct filter_set set;
                if (ibuf_recv_one_filterset(&ibuf, &set) == -1)
                        return -1;
        }
        if (ibuf_size(&ibuf) != 0) {
                errno = EBADMSG;
                return -1;
        }
        return 0;
}