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 "libbpf_internal.h" 16 #include "nlattr.h" 17 18 #ifndef SOL_NETLINK 19 #define SOL_NETLINK 270 20 #endif 21 22 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 23 void *cookie); 24 25 struct xdp_id_md { 26 int ifindex; 27 __u32 flags; 28 struct xdp_link_info info; 29 }; 30 31 int libbpf_netlink_open(__u32 *nl_pid) 32 { 33 struct sockaddr_nl sa; 34 socklen_t addrlen; 35 int one = 1, ret; 36 int sock; 37 38 memset(&sa, 0, sizeof(sa)); 39 sa.nl_family = AF_NETLINK; 40 41 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 42 if (sock < 0) 43 return -errno; 44 45 if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 46 &one, sizeof(one)) < 0) { 47 pr_warn("Netlink error reporting not supported\n"); 48 } 49 50 if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 51 ret = -errno; 52 goto cleanup; 53 } 54 55 addrlen = sizeof(sa); 56 if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 57 ret = -errno; 58 goto cleanup; 59 } 60 61 if (addrlen != sizeof(sa)) { 62 ret = -LIBBPF_ERRNO__INTERNAL; 63 goto cleanup; 64 } 65 66 *nl_pid = sa.nl_pid; 67 return sock; 68 69 cleanup: 70 close(sock); 71 return ret; 72 } 73 74 static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, 75 __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, 76 void *cookie) 77 { 78 bool multipart = true; 79 struct nlmsgerr *err; 80 struct nlmsghdr *nh; 81 char buf[4096]; 82 int len, ret; 83 84 while (multipart) { 85 multipart = false; 86 len = recv(sock, buf, sizeof(buf), 0); 87 if (len < 0) { 88 ret = -errno; 89 goto done; 90 } 91 92 if (len == 0) 93 break; 94 95 for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); 96 nh = NLMSG_NEXT(nh, len)) { 97 if (nh->nlmsg_pid != nl_pid) { 98 ret = -LIBBPF_ERRNO__WRNGPID; 99 goto done; 100 } 101 if (nh->nlmsg_seq != seq) { 102 ret = -LIBBPF_ERRNO__INVSEQ; 103 goto done; 104 } 105 if (nh->nlmsg_flags & NLM_F_MULTI) 106 multipart = true; 107 switch (nh->nlmsg_type) { 108 case NLMSG_ERROR: 109 err = (struct nlmsgerr *)NLMSG_DATA(nh); 110 if (!err->error) 111 continue; 112 ret = err->error; 113 libbpf_nla_dump_errormsg(nh); 114 goto done; 115 case NLMSG_DONE: 116 return 0; 117 default: 118 break; 119 } 120 if (_fn) { 121 ret = _fn(nh, fn, cookie); 122 if (ret) 123 return ret; 124 } 125 } 126 } 127 ret = 0; 128 done: 129 return ret; 130 } 131 132 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 133 { 134 int sock, seq = 0, ret; 135 struct nlattr *nla, *nla_xdp; 136 struct { 137 struct nlmsghdr nh; 138 struct ifinfomsg ifinfo; 139 char attrbuf[64]; 140 } req; 141 __u32 nl_pid; 142 143 sock = libbpf_netlink_open(&nl_pid); 144 if (sock < 0) 145 return sock; 146 147 memset(&req, 0, sizeof(req)); 148 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 149 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 150 req.nh.nlmsg_type = RTM_SETLINK; 151 req.nh.nlmsg_pid = 0; 152 req.nh.nlmsg_seq = ++seq; 153 req.ifinfo.ifi_family = AF_UNSPEC; 154 req.ifinfo.ifi_index = ifindex; 155 156 /* started nested attribute for XDP */ 157 nla = (struct nlattr *)(((char *)&req) 158 + NLMSG_ALIGN(req.nh.nlmsg_len)); 159 nla->nla_type = NLA_F_NESTED | IFLA_XDP; 160 nla->nla_len = NLA_HDRLEN; 161 162 /* add XDP fd */ 163 nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 164 nla_xdp->nla_type = IFLA_XDP_FD; 165 nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); 166 memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); 167 nla->nla_len += nla_xdp->nla_len; 168 169 /* if user passed in any flags, add those too */ 170 if (flags) { 171 nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 172 nla_xdp->nla_type = IFLA_XDP_FLAGS; 173 nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); 174 memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); 175 nla->nla_len += nla_xdp->nla_len; 176 } 177 178 req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); 179 180 if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { 181 ret = -errno; 182 goto cleanup; 183 } 184 ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); 185 186 cleanup: 187 close(sock); 188 return ret; 189 } 190 191 static int __dump_link_nlmsg(struct nlmsghdr *nlh, 192 libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 193 { 194 struct nlattr *tb[IFLA_MAX + 1], *attr; 195 struct ifinfomsg *ifi = NLMSG_DATA(nlh); 196 int len; 197 198 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 199 attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 200 if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 201 return -LIBBPF_ERRNO__NLPARSE; 202 203 return dump_link_nlmsg(cookie, ifi, tb); 204 } 205 206 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) 207 { 208 struct nlattr *xdp_tb[IFLA_XDP_MAX + 1]; 209 struct xdp_id_md *xdp_id = cookie; 210 struct ifinfomsg *ifinfo = msg; 211 int ret; 212 213 if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index) 214 return 0; 215 216 if (!tb[IFLA_XDP]) 217 return 0; 218 219 ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL); 220 if (ret) 221 return ret; 222 223 if (!xdp_tb[IFLA_XDP_ATTACHED]) 224 return 0; 225 226 xdp_id->info.attach_mode = libbpf_nla_getattr_u8( 227 xdp_tb[IFLA_XDP_ATTACHED]); 228 229 if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE) 230 return 0; 231 232 if (xdp_tb[IFLA_XDP_PROG_ID]) 233 xdp_id->info.prog_id = libbpf_nla_getattr_u32( 234 xdp_tb[IFLA_XDP_PROG_ID]); 235 236 if (xdp_tb[IFLA_XDP_SKB_PROG_ID]) 237 xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32( 238 xdp_tb[IFLA_XDP_SKB_PROG_ID]); 239 240 if (xdp_tb[IFLA_XDP_DRV_PROG_ID]) 241 xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32( 242 xdp_tb[IFLA_XDP_DRV_PROG_ID]); 243 244 if (xdp_tb[IFLA_XDP_HW_PROG_ID]) 245 xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32( 246 xdp_tb[IFLA_XDP_HW_PROG_ID]); 247 248 return 0; 249 } 250 251 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, 252 size_t info_size, __u32 flags) 253 { 254 struct xdp_id_md xdp_id = {}; 255 int sock, ret; 256 __u32 nl_pid; 257 __u32 mask; 258 259 if (flags & ~XDP_FLAGS_MASK || !info_size) 260 return -EINVAL; 261 262 /* Check whether the single {HW,DRV,SKB} mode is set */ 263 flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE); 264 mask = flags - 1; 265 if (flags && flags & mask) 266 return -EINVAL; 267 268 sock = libbpf_netlink_open(&nl_pid); 269 if (sock < 0) 270 return sock; 271 272 xdp_id.ifindex = ifindex; 273 xdp_id.flags = flags; 274 275 ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id); 276 if (!ret) { 277 size_t sz = min(info_size, sizeof(xdp_id.info)); 278 279 memcpy(info, &xdp_id.info, sz); 280 memset((void *) info + sz, 0, info_size - sz); 281 } 282 283 close(sock); 284 return ret; 285 } 286 287 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags) 288 { 289 if (info->attach_mode != XDP_ATTACHED_MULTI) 290 return info->prog_id; 291 if (flags & XDP_FLAGS_DRV_MODE) 292 return info->drv_prog_id; 293 if (flags & XDP_FLAGS_HW_MODE) 294 return info->hw_prog_id; 295 if (flags & XDP_FLAGS_SKB_MODE) 296 return info->skb_prog_id; 297 298 return 0; 299 } 300 301 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) 302 { 303 struct xdp_link_info info; 304 int ret; 305 306 ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags); 307 if (!ret) 308 *prog_id = get_xdp_id(&info, flags); 309 310 return ret; 311 } 312 313 int libbpf_nl_get_link(int sock, unsigned int nl_pid, 314 libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 315 { 316 struct { 317 struct nlmsghdr nlh; 318 struct ifinfomsg ifm; 319 } req = { 320 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 321 .nlh.nlmsg_type = RTM_GETLINK, 322 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 323 .ifm.ifi_family = AF_PACKET, 324 }; 325 int seq = time(NULL); 326 327 req.nlh.nlmsg_seq = seq; 328 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 329 return -errno; 330 331 return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, 332 dump_link_nlmsg, cookie); 333 } 334 335 static int __dump_class_nlmsg(struct nlmsghdr *nlh, 336 libbpf_dump_nlmsg_t dump_class_nlmsg, 337 void *cookie) 338 { 339 struct nlattr *tb[TCA_MAX + 1], *attr; 340 struct tcmsg *t = NLMSG_DATA(nlh); 341 int len; 342 343 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 344 attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 345 if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 346 return -LIBBPF_ERRNO__NLPARSE; 347 348 return dump_class_nlmsg(cookie, t, tb); 349 } 350 351 int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex, 352 libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie) 353 { 354 struct { 355 struct nlmsghdr nlh; 356 struct tcmsg t; 357 } req = { 358 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 359 .nlh.nlmsg_type = RTM_GETTCLASS, 360 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 361 .t.tcm_family = AF_UNSPEC, 362 .t.tcm_ifindex = ifindex, 363 }; 364 int seq = time(NULL); 365 366 req.nlh.nlmsg_seq = seq; 367 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 368 return -errno; 369 370 return bpf_netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg, 371 dump_class_nlmsg, cookie); 372 } 373 374 static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh, 375 libbpf_dump_nlmsg_t dump_qdisc_nlmsg, 376 void *cookie) 377 { 378 struct nlattr *tb[TCA_MAX + 1], *attr; 379 struct tcmsg *t = NLMSG_DATA(nlh); 380 int len; 381 382 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 383 attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 384 if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 385 return -LIBBPF_ERRNO__NLPARSE; 386 387 return dump_qdisc_nlmsg(cookie, t, tb); 388 } 389 390 int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex, 391 libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie) 392 { 393 struct { 394 struct nlmsghdr nlh; 395 struct tcmsg t; 396 } req = { 397 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 398 .nlh.nlmsg_type = RTM_GETQDISC, 399 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 400 .t.tcm_family = AF_UNSPEC, 401 .t.tcm_ifindex = ifindex, 402 }; 403 int seq = time(NULL); 404 405 req.nlh.nlmsg_seq = seq; 406 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 407 return -errno; 408 409 return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg, 410 dump_qdisc_nlmsg, cookie); 411 } 412 413 static int __dump_filter_nlmsg(struct nlmsghdr *nlh, 414 libbpf_dump_nlmsg_t dump_filter_nlmsg, 415 void *cookie) 416 { 417 struct nlattr *tb[TCA_MAX + 1], *attr; 418 struct tcmsg *t = NLMSG_DATA(nlh); 419 int len; 420 421 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t)); 422 attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t))); 423 if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0) 424 return -LIBBPF_ERRNO__NLPARSE; 425 426 return dump_filter_nlmsg(cookie, t, tb); 427 } 428 429 int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle, 430 libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie) 431 { 432 struct { 433 struct nlmsghdr nlh; 434 struct tcmsg t; 435 } req = { 436 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), 437 .nlh.nlmsg_type = RTM_GETTFILTER, 438 .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 439 .t.tcm_family = AF_UNSPEC, 440 .t.tcm_ifindex = ifindex, 441 .t.tcm_parent = handle, 442 }; 443 int seq = time(NULL); 444 445 req.nlh.nlmsg_seq = seq; 446 if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) 447 return -errno; 448 449 return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg, 450 dump_filter_nlmsg, cookie); 451 } 452