linux/tools/lib/bpf/netlink.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
   2/* Copyright (c) 2018 Facebook */
   3
   4#include <stdlib.h>
   5#include <memory.h>
   6#include <unistd.h>
   7#include <linux/bpf.h>
   8#include <linux/rtnetlink.h>
   9#include <sys/socket.h>
  10#include <errno.h>
  11#include <time.h>
  12
  13#include "bpf.h"
  14#include "libbpf.h"
  15#include "nlattr.h"
  16
  17#ifndef SOL_NETLINK
  18#define SOL_NETLINK 270
  19#endif
  20
  21typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
  22                              void *cookie);
  23
  24int libbpf_netlink_open(__u32 *nl_pid)
  25{
  26        struct sockaddr_nl sa;
  27        socklen_t addrlen;
  28        int one = 1, ret;
  29        int sock;
  30
  31        memset(&sa, 0, sizeof(sa));
  32        sa.nl_family = AF_NETLINK;
  33
  34        sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  35        if (sock < 0)
  36                return -errno;
  37
  38        if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
  39                       &one, sizeof(one)) < 0) {
  40                fprintf(stderr, "Netlink error reporting not supported\n");
  41        }
  42
  43        if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
  44                ret = -errno;
  45                goto cleanup;
  46        }
  47
  48        addrlen = sizeof(sa);
  49        if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
  50                ret = -errno;
  51                goto cleanup;
  52        }
  53
  54        if (addrlen != sizeof(sa)) {
  55                ret = -LIBBPF_ERRNO__INTERNAL;
  56                goto cleanup;
  57        }
  58
  59        *nl_pid = sa.nl_pid;
  60        return sock;
  61
  62cleanup:
  63        close(sock);
  64        return ret;
  65}
  66
  67static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
  68                            __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
  69                            void *cookie)
  70{
  71        bool multipart = true;
  72        struct nlmsgerr *err;
  73        struct nlmsghdr *nh;
  74        char buf[4096];
  75        int len, ret;
  76
  77        while (multipart) {
  78                multipart = false;
  79                len = recv(sock, buf, sizeof(buf), 0);
  80                if (len < 0) {
  81                        ret = -errno;
  82                        goto done;
  83                }
  84
  85                if (len == 0)
  86                        break;
  87
  88                for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
  89                     nh = NLMSG_NEXT(nh, len)) {
  90                        if (nh->nlmsg_pid != nl_pid) {
  91                                ret = -LIBBPF_ERRNO__WRNGPID;
  92                                goto done;
  93                        }
  94                        if (nh->nlmsg_seq != seq) {
  95                                ret = -LIBBPF_ERRNO__INVSEQ;
  96                                goto done;
  97                        }
  98                        if (nh->nlmsg_flags & NLM_F_MULTI)
  99                                multipart = true;
 100                        switch (nh->nlmsg_type) {
 101                        case NLMSG_ERROR:
 102                                err = (struct nlmsgerr *)NLMSG_DATA(nh);
 103                                if (!err->error)
 104                                        continue;
 105                                ret = err->error;
 106                                libbpf_nla_dump_errormsg(nh);
 107                                goto done;
 108                        case NLMSG_DONE:
 109                                return 0;
 110                        default:
 111                                break;
 112                        }
 113                        if (_fn) {
 114                                ret = _fn(nh, fn, cookie);
 115                                if (ret)
 116                                        return ret;
 117                        }
 118                }
 119        }
 120        ret = 0;
 121done:
 122        return ret;
 123}
 124
 125int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
 126{
 127        int sock, seq = 0, ret;
 128        struct nlattr *nla, *nla_xdp;
 129        struct {
 130                struct nlmsghdr  nh;
 131                struct ifinfomsg ifinfo;
 132                char             attrbuf[64];
 133        } req;
 134        __u32 nl_pid;
 135
 136        sock = libbpf_netlink_open(&nl_pid);
 137        if (sock < 0)
 138                return sock;
 139
 140        memset(&req, 0, sizeof(req));
 141        req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
 142        req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
 143        req.nh.nlmsg_type = RTM_SETLINK;
 144        req.nh.nlmsg_pid = 0;
 145        req.nh.nlmsg_seq = ++seq;
 146        req.ifinfo.ifi_family = AF_UNSPEC;
 147        req.ifinfo.ifi_index = ifindex;
 148
 149        /* started nested attribute for XDP */
 150        nla = (struct nlattr *)(((char *)&req)
 151                                + NLMSG_ALIGN(req.nh.nlmsg_len));
 152        nla->nla_type = NLA_F_NESTED | IFLA_XDP;
 153        nla->nla_len = NLA_HDRLEN;
 154
 155        /* add XDP fd */
 156        nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
 157        nla_xdp->nla_type = IFLA_XDP_FD;
 158        nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
 159        memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
 160        nla->nla_len += nla_xdp->nla_len;
 161
 162        /* if user passed in any flags, add those too */
 163        if (flags) {
 164                nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
 165                nla_xdp->nla_type = IFLA_XDP_FLAGS;
 166                nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
 167                memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
 168                nla->nla_len += nla_xdp->nla_len;
 169        }
 170
 171        req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
 172
 173        if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
 174                ret = -errno;
 175                goto cleanup;
 176        }
 177        ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
 178
 179cleanup:
 180        close(sock);
 181        return ret;
 182}
 183
 184static int __dump_link_nlmsg(struct nlmsghdr *nlh,
 185                             libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
 186{
 187        struct nlattr *tb[IFLA_MAX + 1], *attr;
 188        struct ifinfomsg *ifi = NLMSG_DATA(nlh);
 189        int len;
 190
 191        len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
 192        attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
 193        if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
 194                return -LIBBPF_ERRNO__NLPARSE;
 195
 196        return dump_link_nlmsg(cookie, ifi, tb);
 197}
 198
 199int libbpf_nl_get_link(int sock, unsigned int nl_pid,
 200                       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
 201{
 202        struct {
 203                struct nlmsghdr nlh;
 204                struct ifinfomsg ifm;
 205        } req = {
 206                .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
 207                .nlh.nlmsg_type = RTM_GETLINK,
 208                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 209                .ifm.ifi_family = AF_PACKET,
 210        };
 211        int seq = time(NULL);
 212
 213        req.nlh.nlmsg_seq = seq;
 214        if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 215                return -errno;
 216
 217        return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
 218                                dump_link_nlmsg, cookie);
 219}
 220
 221static int __dump_class_nlmsg(struct nlmsghdr *nlh,
 222                              libbpf_dump_nlmsg_t dump_class_nlmsg,
 223                              void *cookie)
 224{
 225        struct nlattr *tb[TCA_MAX + 1], *attr;
 226        struct tcmsg *t = NLMSG_DATA(nlh);
 227        int len;
 228
 229        len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
 230        attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
 231        if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
 232                return -LIBBPF_ERRNO__NLPARSE;
 233
 234        return dump_class_nlmsg(cookie, t, tb);
 235}
 236
 237int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
 238                        libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie)
 239{
 240        struct {
 241                struct nlmsghdr nlh;
 242                struct tcmsg t;
 243        } req = {
 244                .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 245                .nlh.nlmsg_type = RTM_GETTCLASS,
 246                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 247                .t.tcm_family = AF_UNSPEC,
 248                .t.tcm_ifindex = ifindex,
 249        };
 250        int seq = time(NULL);
 251
 252        req.nlh.nlmsg_seq = seq;
 253        if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 254                return -errno;
 255
 256        return bpf_netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
 257                                dump_class_nlmsg, cookie);
 258}
 259
 260static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
 261                              libbpf_dump_nlmsg_t dump_qdisc_nlmsg,
 262                              void *cookie)
 263{
 264        struct nlattr *tb[TCA_MAX + 1], *attr;
 265        struct tcmsg *t = NLMSG_DATA(nlh);
 266        int len;
 267
 268        len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
 269        attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
 270        if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
 271                return -LIBBPF_ERRNO__NLPARSE;
 272
 273        return dump_qdisc_nlmsg(cookie, t, tb);
 274}
 275
 276int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
 277                        libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
 278{
 279        struct {
 280                struct nlmsghdr nlh;
 281                struct tcmsg t;
 282        } req = {
 283                .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 284                .nlh.nlmsg_type = RTM_GETQDISC,
 285                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 286                .t.tcm_family = AF_UNSPEC,
 287                .t.tcm_ifindex = ifindex,
 288        };
 289        int seq = time(NULL);
 290
 291        req.nlh.nlmsg_seq = seq;
 292        if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 293                return -errno;
 294
 295        return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
 296                                dump_qdisc_nlmsg, cookie);
 297}
 298
 299static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
 300                               libbpf_dump_nlmsg_t dump_filter_nlmsg,
 301                               void *cookie)
 302{
 303        struct nlattr *tb[TCA_MAX + 1], *attr;
 304        struct tcmsg *t = NLMSG_DATA(nlh);
 305        int len;
 306
 307        len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
 308        attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
 309        if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
 310                return -LIBBPF_ERRNO__NLPARSE;
 311
 312        return dump_filter_nlmsg(cookie, t, tb);
 313}
 314
 315int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
 316                         libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie)
 317{
 318        struct {
 319                struct nlmsghdr nlh;
 320                struct tcmsg t;
 321        } req = {
 322                .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 323                .nlh.nlmsg_type = RTM_GETTFILTER,
 324                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
 325                .t.tcm_family = AF_UNSPEC,
 326                .t.tcm_ifindex = ifindex,
 327                .t.tcm_parent = handle,
 328        };
 329        int seq = time(NULL);
 330
 331        req.nlh.nlmsg_seq = seq;
 332        if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
 333                return -errno;
 334
 335        return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
 336                                dump_filter_nlmsg, cookie);
 337}
 338