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