11bc38b8fSAlexei Starovoitov // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2f7010770SYonghong Song /* Copyright (c) 2018 Facebook */ 3f7010770SYonghong Song 4f7010770SYonghong Song #include <stdlib.h> 5f7010770SYonghong Song #include <memory.h> 6f7010770SYonghong Song #include <unistd.h> 7f7010770SYonghong Song #include <linux/bpf.h> 8f7010770SYonghong Song #include <linux/rtnetlink.h> 9f7010770SYonghong Song #include <sys/socket.h> 10f7010770SYonghong Song #include <errno.h> 11f7010770SYonghong Song #include <time.h> 12f7010770SYonghong Song 13f7010770SYonghong Song #include "bpf.h" 14f7010770SYonghong Song #include "libbpf.h" 15f7010770SYonghong Song #include "nlattr.h" 16f7010770SYonghong Song 17f7010770SYonghong Song #ifndef SOL_NETLINK 18f7010770SYonghong Song #define SOL_NETLINK 270 19f7010770SYonghong Song #endif 20f7010770SYonghong Song 21aae57780SAndrey Ignatov typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 22434fe9d4SAndrey Ignatov void *cookie); 23434fe9d4SAndrey Ignatov 24aae57780SAndrey Ignatov int libbpf_netlink_open(__u32 *nl_pid) 25f7010770SYonghong Song { 26f7010770SYonghong Song struct sockaddr_nl sa; 27f7010770SYonghong Song socklen_t addrlen; 28f7010770SYonghong Song int one = 1, ret; 29f7010770SYonghong Song int sock; 30f7010770SYonghong Song 31f7010770SYonghong Song memset(&sa, 0, sizeof(sa)); 32f7010770SYonghong Song sa.nl_family = AF_NETLINK; 33f7010770SYonghong Song 34f7010770SYonghong Song sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 35f7010770SYonghong Song if (sock < 0) 36f7010770SYonghong Song return -errno; 37f7010770SYonghong Song 38f7010770SYonghong Song if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 39f7010770SYonghong Song &one, sizeof(one)) < 0) { 40f7010770SYonghong Song fprintf(stderr, "Netlink error reporting not supported\n"); 41f7010770SYonghong Song } 42f7010770SYonghong Song 43f7010770SYonghong Song if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 44f7010770SYonghong Song ret = -errno; 45f7010770SYonghong Song goto cleanup; 46f7010770SYonghong Song } 47f7010770SYonghong Song 48f7010770SYonghong Song addrlen = sizeof(sa); 49f7010770SYonghong Song if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 50f7010770SYonghong Song ret = -errno; 51f7010770SYonghong Song goto cleanup; 52f7010770SYonghong Song } 53f7010770SYonghong Song 54f7010770SYonghong Song if (addrlen != sizeof(sa)) { 55f7010770SYonghong Song ret = -LIBBPF_ERRNO__INTERNAL; 56f7010770SYonghong Song goto cleanup; 57f7010770SYonghong Song } 58f7010770SYonghong Song 59f7010770SYonghong Song *nl_pid = sa.nl_pid; 60f7010770SYonghong Song return sock; 61f7010770SYonghong Song 62f7010770SYonghong Song cleanup: 63f7010770SYonghong Song close(sock); 64f7010770SYonghong Song return ret; 65f7010770SYonghong Song } 66f7010770SYonghong Song 6736f1678dSYonghong Song static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, 68aae57780SAndrey Ignatov __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, 6936f1678dSYonghong Song void *cookie) 70f7010770SYonghong Song { 719d0b3c1fSYonghong Song bool multipart = true; 72f7010770SYonghong Song struct nlmsgerr *err; 73f7010770SYonghong Song struct nlmsghdr *nh; 74f7010770SYonghong Song char buf[4096]; 75f7010770SYonghong Song int len, ret; 76f7010770SYonghong Song 779d0b3c1fSYonghong Song while (multipart) { 789d0b3c1fSYonghong Song multipart = false; 79f7010770SYonghong Song len = recv(sock, buf, sizeof(buf), 0); 80f7010770SYonghong Song if (len < 0) { 81f7010770SYonghong Song ret = -errno; 82f7010770SYonghong Song goto done; 83f7010770SYonghong Song } 84f7010770SYonghong Song 859d0b3c1fSYonghong Song if (len == 0) 869d0b3c1fSYonghong Song break; 879d0b3c1fSYonghong Song 88f7010770SYonghong Song for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); 89f7010770SYonghong Song nh = NLMSG_NEXT(nh, len)) { 90f7010770SYonghong Song if (nh->nlmsg_pid != nl_pid) { 91f7010770SYonghong Song ret = -LIBBPF_ERRNO__WRNGPID; 92f7010770SYonghong Song goto done; 93f7010770SYonghong Song } 94f7010770SYonghong Song if (nh->nlmsg_seq != seq) { 95f7010770SYonghong Song ret = -LIBBPF_ERRNO__INVSEQ; 96f7010770SYonghong Song goto done; 97f7010770SYonghong Song } 989d0b3c1fSYonghong Song if (nh->nlmsg_flags & NLM_F_MULTI) 999d0b3c1fSYonghong Song multipart = true; 100f7010770SYonghong Song switch (nh->nlmsg_type) { 101f7010770SYonghong Song case NLMSG_ERROR: 102f7010770SYonghong Song err = (struct nlmsgerr *)NLMSG_DATA(nh); 103f7010770SYonghong Song if (!err->error) 104f7010770SYonghong Song continue; 105f7010770SYonghong Song ret = err->error; 106f04bc8a4SAndrey Ignatov libbpf_nla_dump_errormsg(nh); 107f7010770SYonghong Song goto done; 108f7010770SYonghong Song case NLMSG_DONE: 109f7010770SYonghong Song return 0; 110f7010770SYonghong Song default: 111f7010770SYonghong Song break; 112f7010770SYonghong Song } 11336f1678dSYonghong Song if (_fn) { 11436f1678dSYonghong Song ret = _fn(nh, fn, cookie); 11536f1678dSYonghong Song if (ret) 11636f1678dSYonghong Song return ret; 11736f1678dSYonghong Song } 118f7010770SYonghong Song } 119f7010770SYonghong Song } 120f7010770SYonghong Song ret = 0; 121f7010770SYonghong Song done: 122f7010770SYonghong Song return ret; 123f7010770SYonghong Song } 124f7010770SYonghong Song 125f7010770SYonghong Song int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 126f7010770SYonghong Song { 127f7010770SYonghong Song int sock, seq = 0, ret; 128f7010770SYonghong Song struct nlattr *nla, *nla_xdp; 129f7010770SYonghong Song struct { 130f7010770SYonghong Song struct nlmsghdr nh; 131f7010770SYonghong Song struct ifinfomsg ifinfo; 132f7010770SYonghong Song char attrbuf[64]; 133f7010770SYonghong Song } req; 134f7010770SYonghong Song __u32 nl_pid; 135f7010770SYonghong Song 136aae57780SAndrey Ignatov sock = libbpf_netlink_open(&nl_pid); 137f7010770SYonghong Song if (sock < 0) 138f7010770SYonghong Song return sock; 139f7010770SYonghong Song 140f7010770SYonghong Song memset(&req, 0, sizeof(req)); 141f7010770SYonghong Song req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 142f7010770SYonghong Song req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 143f7010770SYonghong Song req.nh.nlmsg_type = RTM_SETLINK; 144f7010770SYonghong Song req.nh.nlmsg_pid = 0; 145f7010770SYonghong Song req.nh.nlmsg_seq = ++seq; 146f7010770SYonghong Song req.ifinfo.ifi_family = AF_UNSPEC; 147f7010770SYonghong Song req.ifinfo.ifi_index = ifindex; 148f7010770SYonghong Song 149f7010770SYonghong Song /* started nested attribute for XDP */ 150f7010770SYonghong Song nla = (struct nlattr *)(((char *)&req) 151f7010770SYonghong Song + NLMSG_ALIGN(req.nh.nlmsg_len)); 152f7010770SYonghong Song nla->nla_type = NLA_F_NESTED | IFLA_XDP; 153f7010770SYonghong Song nla->nla_len = NLA_HDRLEN; 154f7010770SYonghong Song 155f7010770SYonghong Song /* add XDP fd */ 156f7010770SYonghong Song nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 157f7010770SYonghong Song nla_xdp->nla_type = IFLA_XDP_FD; 158f7010770SYonghong Song nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); 159f7010770SYonghong Song memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); 160f7010770SYonghong Song nla->nla_len += nla_xdp->nla_len; 161f7010770SYonghong Song 162f7010770SYonghong Song /* if user passed in any flags, add those too */ 163f7010770SYonghong Song if (flags) { 164f7010770SYonghong Song nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 165f7010770SYonghong Song nla_xdp->nla_type = IFLA_XDP_FLAGS; 166f7010770SYonghong Song nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); 167f7010770SYonghong Song memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); 168f7010770SYonghong Song nla->nla_len += nla_xdp->nla_len; 169f7010770SYonghong Song } 170f7010770SYonghong Song 171f7010770SYonghong Song req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); 172f7010770SYonghong Song 173f7010770SYonghong Song if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { 174f7010770SYonghong Song ret = -errno; 175f7010770SYonghong Song goto cleanup; 176f7010770SYonghong Song } 17736f1678dSYonghong Song ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); 178f7010770SYonghong Song 179f7010770SYonghong Song cleanup: 180f7010770SYonghong Song close(sock); 181f7010770SYonghong Song return ret; 182f7010770SYonghong Song } 18336f1678dSYonghong Song 184aae57780SAndrey Ignatov static int __dump_link_nlmsg(struct nlmsghdr *nlh, 185aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 18636f1678dSYonghong Song { 18736f1678dSYonghong Song struct nlattr *tb[IFLA_MAX + 1], *attr; 18836f1678dSYonghong Song struct ifinfomsg *ifi = NLMSG_DATA(nlh); 18936f1678dSYonghong Song int len; 19036f1678dSYonghong Song 19136f1678dSYonghong Song len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 19236f1678dSYonghong Song attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 193f04bc8a4SAndrey Ignatov if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 19436f1678dSYonghong Song return -LIBBPF_ERRNO__NLPARSE; 19536f1678dSYonghong Song 19636f1678dSYonghong Song return dump_link_nlmsg(cookie, ifi, tb); 19736f1678dSYonghong Song } 19836f1678dSYonghong Song 199aae57780SAndrey Ignatov int libbpf_nl_get_link(int sock, unsigned int nl_pid, 200aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 20136f1678dSYonghong Song { 20236f1678dSYonghong Song struct { 20336f1678dSYonghong Song struct nlmsghdr nlh; 20436f1678dSYonghong Song struct ifinfomsg ifm; 20536f1678dSYonghong Song } req = { 20636f1678dSYonghong Song .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 20736f1678dSYonghong Song .nlh.nlmsg_type = RTM_GETLINK, 20836f1678dSYonghong Song .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 20936f1678dSYonghong Song .ifm.ifi_family = AF_PACKET, 21036f1678dSYonghong Song }; 21136f1678dSYonghong Song int seq = time(NULL); 21236f1678dSYonghong Song 21336f1678dSYonghong Song req.nlh.nlmsg_seq = seq; 21436f1678dSYonghong Song if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 21536f1678dSYonghong Song return -errno; 21636f1678dSYonghong Song 21736f1678dSYonghong Song return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, 21836f1678dSYonghong Song dump_link_nlmsg, cookie); 21936f1678dSYonghong Song } 22036f1678dSYonghong Song 22136f1678dSYonghong Song static int __dump_class_nlmsg(struct nlmsghdr *nlh, 222aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_class_nlmsg, 223aae57780SAndrey Ignatov void *cookie) 22436f1678dSYonghong Song { 22536f1678dSYonghong Song struct nlattr *tb[TCA_MAX + 1], *attr; 22636f1678dSYonghong Song struct tcmsg *t = NLMSG_DATA(nlh); 22736f1678dSYonghong Song int len; 22836f1678dSYonghong Song 22936f1678dSYonghong Song len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 23036f1678dSYonghong Song attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 231f04bc8a4SAndrey Ignatov if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 23236f1678dSYonghong Song return -LIBBPF_ERRNO__NLPARSE; 23336f1678dSYonghong Song 23436f1678dSYonghong Song return dump_class_nlmsg(cookie, t, tb); 23536f1678dSYonghong Song } 23636f1678dSYonghong Song 237aae57780SAndrey Ignatov int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex, 238aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie) 23936f1678dSYonghong Song { 24036f1678dSYonghong Song struct { 24136f1678dSYonghong Song struct nlmsghdr nlh; 24236f1678dSYonghong Song struct tcmsg t; 24336f1678dSYonghong Song } req = { 24436f1678dSYonghong Song .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 24536f1678dSYonghong Song .nlh.nlmsg_type = RTM_GETTCLASS, 24636f1678dSYonghong Song .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 24736f1678dSYonghong Song .t.tcm_family = AF_UNSPEC, 24836f1678dSYonghong Song .t.tcm_ifindex = ifindex, 24936f1678dSYonghong Song }; 25036f1678dSYonghong Song int seq = time(NULL); 25136f1678dSYonghong Song 25236f1678dSYonghong Song req.nlh.nlmsg_seq = seq; 25336f1678dSYonghong Song if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 25436f1678dSYonghong Song return -errno; 25536f1678dSYonghong Song 25636f1678dSYonghong Song return bpf_netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg, 25736f1678dSYonghong Song dump_class_nlmsg, cookie); 25836f1678dSYonghong Song } 25936f1678dSYonghong Song 26036f1678dSYonghong Song static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh, 261aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_qdisc_nlmsg, 262aae57780SAndrey Ignatov void *cookie) 26336f1678dSYonghong Song { 26436f1678dSYonghong Song struct nlattr *tb[TCA_MAX + 1], *attr; 26536f1678dSYonghong Song struct tcmsg *t = NLMSG_DATA(nlh); 26636f1678dSYonghong Song int len; 26736f1678dSYonghong Song 26836f1678dSYonghong Song len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 26936f1678dSYonghong Song attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 270f04bc8a4SAndrey Ignatov if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 27136f1678dSYonghong Song return -LIBBPF_ERRNO__NLPARSE; 27236f1678dSYonghong Song 27336f1678dSYonghong Song return dump_qdisc_nlmsg(cookie, t, tb); 27436f1678dSYonghong Song } 27536f1678dSYonghong Song 276aae57780SAndrey Ignatov int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex, 277aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie) 27836f1678dSYonghong Song { 27936f1678dSYonghong Song struct { 28036f1678dSYonghong Song struct nlmsghdr nlh; 28136f1678dSYonghong Song struct tcmsg t; 28236f1678dSYonghong Song } req = { 28336f1678dSYonghong Song .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 28436f1678dSYonghong Song .nlh.nlmsg_type = RTM_GETQDISC, 28536f1678dSYonghong Song .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 28636f1678dSYonghong Song .t.tcm_family = AF_UNSPEC, 28736f1678dSYonghong Song .t.tcm_ifindex = ifindex, 28836f1678dSYonghong Song }; 28936f1678dSYonghong Song int seq = time(NULL); 29036f1678dSYonghong Song 29136f1678dSYonghong Song req.nlh.nlmsg_seq = seq; 29236f1678dSYonghong Song if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 29336f1678dSYonghong Song return -errno; 29436f1678dSYonghong Song 29536f1678dSYonghong Song return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg, 29636f1678dSYonghong Song dump_qdisc_nlmsg, cookie); 29736f1678dSYonghong Song } 29836f1678dSYonghong Song 29936f1678dSYonghong Song static int __dump_filter_nlmsg(struct nlmsghdr *nlh, 300aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_filter_nlmsg, 301aae57780SAndrey Ignatov void *cookie) 30236f1678dSYonghong Song { 30336f1678dSYonghong Song struct nlattr *tb[TCA_MAX + 1], *attr; 30436f1678dSYonghong Song struct tcmsg *t = NLMSG_DATA(nlh); 30536f1678dSYonghong Song int len; 30636f1678dSYonghong Song 30736f1678dSYonghong Song len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 30836f1678dSYonghong Song attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 309f04bc8a4SAndrey Ignatov if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 31036f1678dSYonghong Song return -LIBBPF_ERRNO__NLPARSE; 31136f1678dSYonghong Song 31236f1678dSYonghong Song return dump_filter_nlmsg(cookie, t, tb); 31336f1678dSYonghong Song } 31436f1678dSYonghong Song 315aae57780SAndrey Ignatov int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle, 316aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie) 31736f1678dSYonghong Song { 31836f1678dSYonghong Song struct { 31936f1678dSYonghong Song struct nlmsghdr nlh; 32036f1678dSYonghong Song struct tcmsg t; 32136f1678dSYonghong Song } req = { 32236f1678dSYonghong Song .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 32336f1678dSYonghong Song .nlh.nlmsg_type = RTM_GETTFILTER, 32436f1678dSYonghong Song .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 32536f1678dSYonghong Song .t.tcm_family = AF_UNSPEC, 32636f1678dSYonghong Song .t.tcm_ifindex = ifindex, 32736f1678dSYonghong Song .t.tcm_parent = handle, 32836f1678dSYonghong Song }; 32936f1678dSYonghong Song int seq = time(NULL); 33036f1678dSYonghong Song 33136f1678dSYonghong Song req.nlh.nlmsg_seq = seq; 33236f1678dSYonghong Song if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 33336f1678dSYonghong Song return -errno; 33436f1678dSYonghong Song 33536f1678dSYonghong Song return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg, 33636f1678dSYonghong Song dump_filter_nlmsg, cookie); 33736f1678dSYonghong Song } 338