netlink.c 22 KB

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