root/tools/net/ynl/samples/rt-link.c
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <string.h>

#include <ynl.h>

#include <arpa/inet.h>
#include <net/if.h>

#include "rt-link-user.h"

static void rt_link_print(struct rt_link_getlink_rsp *r)
{
        unsigned int i;

        printf("%3d: ", r->_hdr.ifi_index);

        if (r->_len.ifname)
                printf("%16s: ", r->ifname);

        if (r->_present.mtu)
                printf("mtu %5d  ", r->mtu);

        if (r->linkinfo._len.kind)
                printf("kind %-8s  ", r->linkinfo.kind);
        else
                printf("     %8s  ", "");

        if (r->prop_list._count.alt_ifname) {
                printf("altname ");
                for (i = 0; i < r->prop_list._count.alt_ifname; i++)
                        printf("%s ", r->prop_list.alt_ifname[i]->str);
                printf(" ");
        }

        if (r->linkinfo._present.data && r->linkinfo.data._present.netkit) {
                struct rt_link_linkinfo_netkit_attrs *netkit;
                const char *name;

                netkit = &r->linkinfo.data.netkit;
                printf("primary %d  ", netkit->primary);

                name = NULL;
                if (netkit->_present.policy)
                        name = rt_link_netkit_policy_str(netkit->policy);
                if (name)
                        printf("policy %s  ", name);
        }

        printf("\n");
}

static int rt_link_create_netkit(struct ynl_sock *ys)
{
        struct rt_link_getlink_ntf *ntf_gl;
        struct rt_link_newlink_req *req;
        struct ynl_ntf_base_type *ntf;
        int ret;

        req = rt_link_newlink_req_alloc();
        if (!req) {
                fprintf(stderr, "Can't alloc req\n");
                return -1;
        }

        /* rtnetlink doesn't provide info about the created object.
         * It expects us to set the ECHO flag and the dig the info out
         * of the notifications...
         */
        rt_link_newlink_req_set_nlflags(req, NLM_F_CREATE | NLM_F_ECHO);

        rt_link_newlink_req_set_linkinfo_kind(req, "netkit");

        /* Test error messages */
        rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, 10);
        ret = rt_link_newlink(ys, req);
        if (ret) {
                printf("Testing error message for policy being bad:\n\t%s\n", ys->err.msg);
        } else {
                fprintf(stderr, "Warning: unexpected success creating netkit with bad attrs\n");
                goto created;
        }

        rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, NETKIT_DROP);

        ret = rt_link_newlink(ys, req);
created:
        rt_link_newlink_req_free(req);
        if (ret) {
                fprintf(stderr, "YNL: %s\n", ys->err.msg);
                return -1;
        }

        if (!ynl_has_ntf(ys)) {
                fprintf(stderr,
                        "Warning: interface created but received no notification, won't delete the interface\n");
                return 0;
        }

        ntf = ynl_ntf_dequeue(ys);
        if (ntf->cmd != RTM_NEWLINK) {
                fprintf(stderr,
                        "Warning: unexpected notification type, won't delete the interface\n");
                return 0;
        }
        ntf_gl = (void *)ntf;
        ret = ntf_gl->obj._hdr.ifi_index;
        ynl_ntf_free(ntf);

        return ret;
}

static void rt_link_del(struct ynl_sock *ys, int ifindex)
{
        struct rt_link_dellink_req *req;

        req = rt_link_dellink_req_alloc();
        if (!req) {
                fprintf(stderr, "Can't alloc req\n");
                return;
        }

        req->_hdr.ifi_index = ifindex;
        if (rt_link_dellink(ys, req))
                fprintf(stderr, "YNL: %s\n", ys->err.msg);
        else
                fprintf(stderr,
                        "Trying to delete a Netkit interface (ifindex %d)\n",
                        ifindex);

        rt_link_dellink_req_free(req);
}

int main(int argc, char **argv)
{
        struct rt_link_getlink_req_dump *req;
        struct rt_link_getlink_list *rsp;
        struct ynl_error yerr;
        struct ynl_sock *ys;
        int created = 0;

        ys = ynl_sock_create(&ynl_rt_link_family, &yerr);
        if (!ys) {
                fprintf(stderr, "YNL: %s\n", yerr.msg);
                return 1;
        }

        if (argc > 1) {
                fprintf(stderr, "Trying to create a Netkit interface\n");
                created = rt_link_create_netkit(ys);
                if (created < 0)
                        goto err_destroy;
        }

        req = rt_link_getlink_req_dump_alloc();
        if (!req)
                goto err_del_ifc;

        rsp = rt_link_getlink_dump(ys, req);
        rt_link_getlink_req_dump_free(req);
        if (!rsp)
                goto err_close;

        if (ynl_dump_empty(rsp))
                fprintf(stderr, "Error: no links reported\n");
        ynl_dump_foreach(rsp, link)
                rt_link_print(link);
        rt_link_getlink_list_free(rsp);

        if (created)
                rt_link_del(ys, created);

        ynl_sock_destroy(ys);
        return 0;

err_close:
        fprintf(stderr, "YNL: %s\n", ys->err.msg);
err_del_ifc:
        if (created)
                rt_link_del(ys, created);
err_destroy:
        ynl_sock_destroy(ys);
        return 2;
}