netlink.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
  2. /* Copyright (c) 2018 Facebook */
  3. #include <stdlib.h>
  4. #include <memory.h>
  5. #include <unistd.h>
  6. #include <arpa/inet.h>
  7. #include <linux/bpf.h>
  8. #include <linux/if_ether.h>
  9. #include <linux/pkt_cls.h>
  10. #include <linux/rtnetlink.h>
  11. #include <sys/socket.h>
  12. #include <errno.h>
  13. #include <time.h>
  14. #include "bpf.h"
  15. #include "libbpf.h"
  16. #include "libbpf_internal.h"
  17. #include "nlattr.h"
  18. #ifndef SOL_NETLINK
  19. #define SOL_NETLINK 270
  20. #endif
  21. typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
  22. typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
  23. void *cookie);
  24. struct xdp_link_info {
  25. __u32 prog_id;
  26. __u32 drv_prog_id;
  27. __u32 hw_prog_id;
  28. __u32 skb_prog_id;
  29. __u8 attach_mode;
  30. };
  31. struct xdp_id_md {
  32. int ifindex;
  33. __u32 flags;
  34. struct xdp_link_info info;
  35. };
  36. static int libbpf_netlink_open(__u32 *nl_pid)
  37. {
  38. struct sockaddr_nl sa;
  39. socklen_t addrlen;
  40. int one = 1, ret;
  41. int sock;
  42. memset(&sa, 0, sizeof(sa));
  43. sa.nl_family = AF_NETLINK;
  44. sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
  45. if (sock < 0)
  46. return -errno;
  47. if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
  48. &one, sizeof(one)) < 0) {
  49. pr_warn("Netlink error reporting not supported\n");
  50. }
  51. if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
  52. ret = -errno;
  53. goto cleanup;
  54. }
  55. addrlen = sizeof(sa);
  56. if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
  57. ret = -errno;
  58. goto cleanup;
  59. }
  60. if (addrlen != sizeof(sa)) {
  61. ret = -LIBBPF_ERRNO__INTERNAL;
  62. goto cleanup;
  63. }
  64. *nl_pid = sa.nl_pid;
  65. return sock;
  66. cleanup:
  67. close(sock);
  68. return ret;
  69. }
  70. static void libbpf_netlink_close(int sock)
  71. {
  72. close(sock);
  73. }
  74. enum {
  75. NL_CONT,
  76. NL_NEXT,
  77. NL_DONE,
  78. };
  79. static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
  80. {
  81. int len;
  82. do {
  83. len = recvmsg(sock, mhdr, flags);
  84. } while (len < 0 && (errno == EINTR || errno == EAGAIN));
  85. if (len < 0)
  86. return -errno;
  87. return len;
  88. }
  89. static int alloc_iov(struct iovec *iov, int len)
  90. {
  91. void *nbuf;
  92. nbuf = realloc(iov->iov_base, len);
  93. if (!nbuf)
  94. return -ENOMEM;
  95. iov->iov_base = nbuf;
  96. iov->iov_len = len;
  97. return 0;
  98. }
  99. static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
  100. __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
  101. void *cookie)
  102. {
  103. struct iovec iov = {};
  104. struct msghdr mhdr = {
  105. .msg_iov = &iov,
  106. .msg_iovlen = 1,
  107. };
  108. bool multipart = true;
  109. struct nlmsgerr *err;
  110. struct nlmsghdr *nh;
  111. int len, ret;
  112. ret = alloc_iov(&iov, 4096);
  113. if (ret)
  114. goto done;
  115. while (multipart) {
  116. start:
  117. multipart = false;
  118. len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
  119. if (len < 0) {
  120. ret = len;
  121. goto done;
  122. }
  123. if (len > iov.iov_len) {
  124. ret = alloc_iov(&iov, len);
  125. if (ret)
  126. goto done;
  127. }
  128. len = netlink_recvmsg(sock, &mhdr, 0);
  129. if (len < 0) {
  130. ret = len;
  131. goto done;
  132. }
  133. if (len == 0)
  134. break;
  135. for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
  136. nh = NLMSG_NEXT(nh, len)) {
  137. if (nh->nlmsg_pid != nl_pid) {
  138. ret = -LIBBPF_ERRNO__WRNGPID;
  139. goto done;
  140. }
  141. if (nh->nlmsg_seq != seq) {
  142. ret = -LIBBPF_ERRNO__INVSEQ;
  143. goto done;
  144. }
  145. if (nh->nlmsg_flags & NLM_F_MULTI)
  146. multipart = true;
  147. switch (nh->nlmsg_type) {
  148. case NLMSG_ERROR:
  149. err = (struct nlmsgerr *)NLMSG_DATA(nh);
  150. if (!err->error)
  151. continue;
  152. ret = err->error;
  153. libbpf_nla_dump_errormsg(nh);
  154. goto done;
  155. case NLMSG_DONE:
  156. ret = 0;
  157. goto done;
  158. default:
  159. break;
  160. }
  161. if (_fn) {
  162. ret = _fn(nh, fn, cookie);
  163. switch (ret) {
  164. case NL_CONT:
  165. break;
  166. case NL_NEXT:
  167. goto start;
  168. case NL_DONE:
  169. ret = 0;
  170. goto done;
  171. default:
  172. goto done;
  173. }
  174. }
  175. }
  176. }
  177. ret = 0;
  178. done:
  179. free(iov.iov_base);
  180. return ret;
  181. }
  182. static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
  183. __dump_nlmsg_t parse_msg,
  184. libbpf_dump_nlmsg_t parse_attr,
  185. void *cookie)
  186. {
  187. __u32 nl_pid = 0;
  188. int sock, ret;
  189. sock = libbpf_netlink_open(&nl_pid);
  190. if (sock < 0)
  191. return sock;
  192. req->nh.nlmsg_pid = 0;
  193. req->nh.nlmsg_seq = time(NULL);
  194. if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
  195. ret = -errno;
  196. goto out;
  197. }
  198. ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
  199. parse_msg, parse_attr, cookie);
  200. out:
  201. libbpf_netlink_close(sock);
  202. return ret;
  203. }
  204. static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
  205. __u32 flags)
  206. {
  207. struct nlattr *nla;
  208. int ret;
  209. struct libbpf_nla_req req;
  210. memset(&req, 0, sizeof(req));
  211. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
  212. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  213. req.nh.nlmsg_type = RTM_SETLINK;
  214. req.ifinfo.ifi_family = AF_UNSPEC;
  215. req.ifinfo.ifi_index = ifindex;
  216. nla = nlattr_begin_nested(&req, IFLA_XDP);
  217. if (!nla)
  218. return -EMSGSIZE;
  219. ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
  220. if (ret < 0)
  221. return ret;
  222. if (flags) {
  223. ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
  224. if (ret < 0)
  225. return ret;
  226. }
  227. if (flags & XDP_FLAGS_REPLACE) {
  228. ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
  229. sizeof(old_fd));
  230. if (ret < 0)
  231. return ret;
  232. }
  233. nlattr_end_nested(&req, nla);
  234. return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
  235. }
  236. int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
  237. {
  238. int old_prog_fd, err;
  239. if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
  240. return libbpf_err(-EINVAL);
  241. old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
  242. if (old_prog_fd)
  243. flags |= XDP_FLAGS_REPLACE;
  244. else
  245. old_prog_fd = -1;
  246. err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
  247. return libbpf_err(err);
  248. }
  249. int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
  250. {
  251. return bpf_xdp_attach(ifindex, -1, flags, opts);
  252. }
  253. static int __dump_link_nlmsg(struct nlmsghdr *nlh,
  254. libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
  255. {
  256. struct nlattr *tb[IFLA_MAX + 1], *attr;
  257. struct ifinfomsg *ifi = NLMSG_DATA(nlh);
  258. int len;
  259. len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
  260. attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
  261. if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
  262. return -LIBBPF_ERRNO__NLPARSE;
  263. return dump_link_nlmsg(cookie, ifi, tb);
  264. }
  265. static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
  266. {
  267. struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
  268. struct xdp_id_md *xdp_id = cookie;
  269. struct ifinfomsg *ifinfo = msg;
  270. int ret;
  271. if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
  272. return 0;
  273. if (!tb[IFLA_XDP])
  274. return 0;
  275. ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
  276. if (ret)
  277. return ret;
  278. if (!xdp_tb[IFLA_XDP_ATTACHED])
  279. return 0;
  280. xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
  281. xdp_tb[IFLA_XDP_ATTACHED]);
  282. if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
  283. return 0;
  284. if (xdp_tb[IFLA_XDP_PROG_ID])
  285. xdp_id->info.prog_id = libbpf_nla_getattr_u32(
  286. xdp_tb[IFLA_XDP_PROG_ID]);
  287. if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
  288. xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
  289. xdp_tb[IFLA_XDP_SKB_PROG_ID]);
  290. if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
  291. xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
  292. xdp_tb[IFLA_XDP_DRV_PROG_ID]);
  293. if (xdp_tb[IFLA_XDP_HW_PROG_ID])
  294. xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
  295. xdp_tb[IFLA_XDP_HW_PROG_ID]);
  296. return 0;
  297. }
  298. int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
  299. {
  300. struct libbpf_nla_req req = {
  301. .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
  302. .nh.nlmsg_type = RTM_GETLINK,
  303. .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
  304. .ifinfo.ifi_family = AF_PACKET,
  305. };
  306. struct xdp_id_md xdp_id = {};
  307. int err;
  308. if (!OPTS_VALID(opts, bpf_xdp_query_opts))
  309. return libbpf_err(-EINVAL);
  310. if (xdp_flags & ~XDP_FLAGS_MASK)
  311. return libbpf_err(-EINVAL);
  312. /* Check whether the single {HW,DRV,SKB} mode is set */
  313. xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
  314. if (xdp_flags & (xdp_flags - 1))
  315. return libbpf_err(-EINVAL);
  316. xdp_id.ifindex = ifindex;
  317. xdp_id.flags = xdp_flags;
  318. err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg,
  319. get_xdp_info, &xdp_id);
  320. if (err)
  321. return libbpf_err(err);
  322. OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
  323. OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
  324. OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
  325. OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
  326. OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
  327. return 0;
  328. }
  329. int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
  330. {
  331. LIBBPF_OPTS(bpf_xdp_query_opts, opts);
  332. int ret;
  333. ret = bpf_xdp_query(ifindex, flags, &opts);
  334. if (ret)
  335. return libbpf_err(ret);
  336. flags &= XDP_FLAGS_MODES;
  337. if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
  338. *prog_id = opts.prog_id;
  339. else if (flags & XDP_FLAGS_DRV_MODE)
  340. *prog_id = opts.drv_prog_id;
  341. else if (flags & XDP_FLAGS_HW_MODE)
  342. *prog_id = opts.hw_prog_id;
  343. else if (flags & XDP_FLAGS_SKB_MODE)
  344. *prog_id = opts.skb_prog_id;
  345. else
  346. *prog_id = 0;
  347. return 0;
  348. }
  349. typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
  350. static int clsact_config(struct libbpf_nla_req *req)
  351. {
  352. req->tc.tcm_parent = TC_H_CLSACT;
  353. req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
  354. return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
  355. }
  356. static int attach_point_to_config(struct bpf_tc_hook *hook,
  357. qdisc_config_t *config)
  358. {
  359. switch (OPTS_GET(hook, attach_point, 0)) {
  360. case BPF_TC_INGRESS:
  361. case BPF_TC_EGRESS:
  362. case BPF_TC_INGRESS | BPF_TC_EGRESS:
  363. if (OPTS_GET(hook, parent, 0))
  364. return -EINVAL;
  365. *config = &clsact_config;
  366. return 0;
  367. case BPF_TC_CUSTOM:
  368. return -EOPNOTSUPP;
  369. default:
  370. return -EINVAL;
  371. }
  372. }
  373. static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
  374. __u32 *parent)
  375. {
  376. switch (attach_point) {
  377. case BPF_TC_INGRESS:
  378. case BPF_TC_EGRESS:
  379. if (*parent)
  380. return -EINVAL;
  381. *parent = TC_H_MAKE(TC_H_CLSACT,
  382. attach_point == BPF_TC_INGRESS ?
  383. TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
  384. break;
  385. case BPF_TC_CUSTOM:
  386. if (!*parent)
  387. return -EINVAL;
  388. break;
  389. default:
  390. return -EINVAL;
  391. }
  392. return 0;
  393. }
  394. static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
  395. {
  396. qdisc_config_t config;
  397. int ret;
  398. struct libbpf_nla_req req;
  399. ret = attach_point_to_config(hook, &config);
  400. if (ret < 0)
  401. return ret;
  402. memset(&req, 0, sizeof(req));
  403. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  404. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
  405. req.nh.nlmsg_type = cmd;
  406. req.tc.tcm_family = AF_UNSPEC;
  407. req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
  408. ret = config(&req);
  409. if (ret < 0)
  410. return ret;
  411. return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
  412. }
  413. static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
  414. {
  415. return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
  416. }
  417. static int tc_qdisc_delete(struct bpf_tc_hook *hook)
  418. {
  419. return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
  420. }
  421. int bpf_tc_hook_create(struct bpf_tc_hook *hook)
  422. {
  423. int ret;
  424. if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
  425. OPTS_GET(hook, ifindex, 0) <= 0)
  426. return libbpf_err(-EINVAL);
  427. ret = tc_qdisc_create_excl(hook);
  428. return libbpf_err(ret);
  429. }
  430. static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
  431. const struct bpf_tc_opts *opts,
  432. const bool flush);
  433. int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
  434. {
  435. if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
  436. OPTS_GET(hook, ifindex, 0) <= 0)
  437. return libbpf_err(-EINVAL);
  438. switch (OPTS_GET(hook, attach_point, 0)) {
  439. case BPF_TC_INGRESS:
  440. case BPF_TC_EGRESS:
  441. return libbpf_err(__bpf_tc_detach(hook, NULL, true));
  442. case BPF_TC_INGRESS | BPF_TC_EGRESS:
  443. return libbpf_err(tc_qdisc_delete(hook));
  444. case BPF_TC_CUSTOM:
  445. return libbpf_err(-EOPNOTSUPP);
  446. default:
  447. return libbpf_err(-EINVAL);
  448. }
  449. }
  450. struct bpf_cb_ctx {
  451. struct bpf_tc_opts *opts;
  452. bool processed;
  453. };
  454. static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
  455. bool unicast)
  456. {
  457. struct nlattr *tbb[TCA_BPF_MAX + 1];
  458. struct bpf_cb_ctx *info = cookie;
  459. if (!info || !info->opts)
  460. return -EINVAL;
  461. if (unicast && info->processed)
  462. return -EINVAL;
  463. if (!tb[TCA_OPTIONS])
  464. return NL_CONT;
  465. libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
  466. if (!tbb[TCA_BPF_ID])
  467. return -EINVAL;
  468. OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
  469. OPTS_SET(info->opts, handle, tc->tcm_handle);
  470. OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
  471. info->processed = true;
  472. return unicast ? NL_NEXT : NL_DONE;
  473. }
  474. static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
  475. void *cookie)
  476. {
  477. struct tcmsg *tc = NLMSG_DATA(nh);
  478. struct nlattr *tb[TCA_MAX + 1];
  479. libbpf_nla_parse(tb, TCA_MAX,
  480. (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
  481. NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
  482. if (!tb[TCA_KIND])
  483. return NL_CONT;
  484. return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
  485. }
  486. static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
  487. {
  488. struct bpf_prog_info info;
  489. __u32 info_len = sizeof(info);
  490. char name[256];
  491. int len, ret;
  492. memset(&info, 0, info_len);
  493. ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
  494. if (ret < 0)
  495. return ret;
  496. ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
  497. if (ret < 0)
  498. return ret;
  499. len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
  500. if (len < 0)
  501. return -errno;
  502. if (len >= sizeof(name))
  503. return -ENAMETOOLONG;
  504. return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
  505. }
  506. int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
  507. {
  508. __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
  509. int ret, ifindex, attach_point, prog_fd;
  510. struct bpf_cb_ctx info = {};
  511. struct libbpf_nla_req req;
  512. struct nlattr *nla;
  513. if (!hook || !opts ||
  514. !OPTS_VALID(hook, bpf_tc_hook) ||
  515. !OPTS_VALID(opts, bpf_tc_opts))
  516. return libbpf_err(-EINVAL);
  517. ifindex = OPTS_GET(hook, ifindex, 0);
  518. parent = OPTS_GET(hook, parent, 0);
  519. attach_point = OPTS_GET(hook, attach_point, 0);
  520. handle = OPTS_GET(opts, handle, 0);
  521. priority = OPTS_GET(opts, priority, 0);
  522. prog_fd = OPTS_GET(opts, prog_fd, 0);
  523. prog_id = OPTS_GET(opts, prog_id, 0);
  524. flags = OPTS_GET(opts, flags, 0);
  525. if (ifindex <= 0 || !prog_fd || prog_id)
  526. return libbpf_err(-EINVAL);
  527. if (priority > UINT16_MAX)
  528. return libbpf_err(-EINVAL);
  529. if (flags & ~BPF_TC_F_REPLACE)
  530. return libbpf_err(-EINVAL);
  531. flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
  532. protocol = ETH_P_ALL;
  533. memset(&req, 0, sizeof(req));
  534. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  535. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
  536. NLM_F_ECHO | flags;
  537. req.nh.nlmsg_type = RTM_NEWTFILTER;
  538. req.tc.tcm_family = AF_UNSPEC;
  539. req.tc.tcm_ifindex = ifindex;
  540. req.tc.tcm_handle = handle;
  541. req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
  542. ret = tc_get_tcm_parent(attach_point, &parent);
  543. if (ret < 0)
  544. return libbpf_err(ret);
  545. req.tc.tcm_parent = parent;
  546. ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
  547. if (ret < 0)
  548. return libbpf_err(ret);
  549. nla = nlattr_begin_nested(&req, TCA_OPTIONS);
  550. if (!nla)
  551. return libbpf_err(-EMSGSIZE);
  552. ret = tc_add_fd_and_name(&req, prog_fd);
  553. if (ret < 0)
  554. return libbpf_err(ret);
  555. bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
  556. ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
  557. if (ret < 0)
  558. return libbpf_err(ret);
  559. nlattr_end_nested(&req, nla);
  560. info.opts = opts;
  561. ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
  562. if (ret < 0)
  563. return libbpf_err(ret);
  564. if (!info.processed)
  565. return libbpf_err(-ENOENT);
  566. return ret;
  567. }
  568. static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
  569. const struct bpf_tc_opts *opts,
  570. const bool flush)
  571. {
  572. __u32 protocol = 0, handle, priority, parent, prog_id, flags;
  573. int ret, ifindex, attach_point, prog_fd;
  574. struct libbpf_nla_req req;
  575. if (!hook ||
  576. !OPTS_VALID(hook, bpf_tc_hook) ||
  577. !OPTS_VALID(opts, bpf_tc_opts))
  578. return -EINVAL;
  579. ifindex = OPTS_GET(hook, ifindex, 0);
  580. parent = OPTS_GET(hook, parent, 0);
  581. attach_point = OPTS_GET(hook, attach_point, 0);
  582. handle = OPTS_GET(opts, handle, 0);
  583. priority = OPTS_GET(opts, priority, 0);
  584. prog_fd = OPTS_GET(opts, prog_fd, 0);
  585. prog_id = OPTS_GET(opts, prog_id, 0);
  586. flags = OPTS_GET(opts, flags, 0);
  587. if (ifindex <= 0 || flags || prog_fd || prog_id)
  588. return -EINVAL;
  589. if (priority > UINT16_MAX)
  590. return -EINVAL;
  591. if (!flush) {
  592. if (!handle || !priority)
  593. return -EINVAL;
  594. protocol = ETH_P_ALL;
  595. } else {
  596. if (handle || priority)
  597. return -EINVAL;
  598. }
  599. memset(&req, 0, sizeof(req));
  600. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  601. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  602. req.nh.nlmsg_type = RTM_DELTFILTER;
  603. req.tc.tcm_family = AF_UNSPEC;
  604. req.tc.tcm_ifindex = ifindex;
  605. if (!flush) {
  606. req.tc.tcm_handle = handle;
  607. req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
  608. }
  609. ret = tc_get_tcm_parent(attach_point, &parent);
  610. if (ret < 0)
  611. return ret;
  612. req.tc.tcm_parent = parent;
  613. if (!flush) {
  614. ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
  615. if (ret < 0)
  616. return ret;
  617. }
  618. return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
  619. }
  620. int bpf_tc_detach(const struct bpf_tc_hook *hook,
  621. const struct bpf_tc_opts *opts)
  622. {
  623. int ret;
  624. if (!opts)
  625. return libbpf_err(-EINVAL);
  626. ret = __bpf_tc_detach(hook, opts, false);
  627. return libbpf_err(ret);
  628. }
  629. int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
  630. {
  631. __u32 protocol, handle, priority, parent, prog_id, flags;
  632. int ret, ifindex, attach_point, prog_fd;
  633. struct bpf_cb_ctx info = {};
  634. struct libbpf_nla_req req;
  635. if (!hook || !opts ||
  636. !OPTS_VALID(hook, bpf_tc_hook) ||
  637. !OPTS_VALID(opts, bpf_tc_opts))
  638. return libbpf_err(-EINVAL);
  639. ifindex = OPTS_GET(hook, ifindex, 0);
  640. parent = OPTS_GET(hook, parent, 0);
  641. attach_point = OPTS_GET(hook, attach_point, 0);
  642. handle = OPTS_GET(opts, handle, 0);
  643. priority = OPTS_GET(opts, priority, 0);
  644. prog_fd = OPTS_GET(opts, prog_fd, 0);
  645. prog_id = OPTS_GET(opts, prog_id, 0);
  646. flags = OPTS_GET(opts, flags, 0);
  647. if (ifindex <= 0 || flags || prog_fd || prog_id ||
  648. !handle || !priority)
  649. return libbpf_err(-EINVAL);
  650. if (priority > UINT16_MAX)
  651. return libbpf_err(-EINVAL);
  652. protocol = ETH_P_ALL;
  653. memset(&req, 0, sizeof(req));
  654. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  655. req.nh.nlmsg_flags = NLM_F_REQUEST;
  656. req.nh.nlmsg_type = RTM_GETTFILTER;
  657. req.tc.tcm_family = AF_UNSPEC;
  658. req.tc.tcm_ifindex = ifindex;
  659. req.tc.tcm_handle = handle;
  660. req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
  661. ret = tc_get_tcm_parent(attach_point, &parent);
  662. if (ret < 0)
  663. return libbpf_err(ret);
  664. req.tc.tcm_parent = parent;
  665. ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
  666. if (ret < 0)
  667. return libbpf_err(ret);
  668. info.opts = opts;
  669. ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
  670. if (ret < 0)
  671. return libbpf_err(ret);
  672. if (!info.processed)
  673. return libbpf_err(-ENOENT);
  674. return ret;
  675. }