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 21 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 22 void *cookie); 23 24 int 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 62 cleanup: 63 close(sock); 64 return ret; 65 } 66 67 static 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; 121 done: 122 return ret; 123 } 124 125 int 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 179 cleanup: 180 close(sock); 181 return ret; 182 } 183 184 static 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 199 int 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 221 static 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 237 int 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 260 static 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 276 int 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 299 static 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 315 int 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