netlink.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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, 8192);
  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. if (len)
  184. pr_warn("Invalid message or trailing data in Netlink response: %d bytes left\n", len);
  185. }
  186. ret = 0;
  187. done:
  188. free(iov.iov_base);
  189. return ret;
  190. }
  191. static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
  192. int proto, __dump_nlmsg_t parse_msg,
  193. libbpf_dump_nlmsg_t parse_attr,
  194. void *cookie)
  195. {
  196. __u32 nl_pid = 0;
  197. int sock, ret;
  198. sock = libbpf_netlink_open(&nl_pid, proto);
  199. if (sock < 0)
  200. return sock;
  201. req->nh.nlmsg_pid = 0;
  202. req->nh.nlmsg_seq = time(NULL);
  203. if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
  204. ret = -errno;
  205. goto out;
  206. }
  207. ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
  208. parse_msg, parse_attr, cookie);
  209. out:
  210. libbpf_netlink_close(sock);
  211. return ret;
  212. }
  213. static int parse_genl_family_id(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
  214. void *cookie)
  215. {
  216. struct genlmsghdr *gnl = NLMSG_DATA(nh);
  217. struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
  218. struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
  219. __u16 *id = cookie;
  220. libbpf_nla_parse(tb, CTRL_ATTR_FAMILY_ID, na,
  221. NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
  222. if (!tb[CTRL_ATTR_FAMILY_ID])
  223. return NL_CONT;
  224. *id = libbpf_nla_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
  225. return NL_DONE;
  226. }
  227. static int libbpf_netlink_resolve_genl_family_id(const char *name,
  228. __u16 len, __u16 *id)
  229. {
  230. struct libbpf_nla_req req = {
  231. .nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
  232. .nh.nlmsg_type = GENL_ID_CTRL,
  233. .nh.nlmsg_flags = NLM_F_REQUEST,
  234. .gnl.cmd = CTRL_CMD_GETFAMILY,
  235. .gnl.version = 2,
  236. };
  237. int err;
  238. err = nlattr_add(&req, CTRL_ATTR_FAMILY_NAME, name, len);
  239. if (err < 0)
  240. return err;
  241. return libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
  242. parse_genl_family_id, NULL, id);
  243. }
  244. static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
  245. __u32 flags)
  246. {
  247. struct nlattr *nla;
  248. int ret;
  249. struct libbpf_nla_req req;
  250. memset(&req, 0, sizeof(req));
  251. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
  252. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  253. req.nh.nlmsg_type = RTM_SETLINK;
  254. req.ifinfo.ifi_family = AF_UNSPEC;
  255. req.ifinfo.ifi_index = ifindex;
  256. nla = nlattr_begin_nested(&req, IFLA_XDP);
  257. if (!nla)
  258. return -EMSGSIZE;
  259. ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
  260. if (ret < 0)
  261. return ret;
  262. if (flags) {
  263. ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
  264. if (ret < 0)
  265. return ret;
  266. }
  267. if (flags & XDP_FLAGS_REPLACE) {
  268. ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
  269. sizeof(old_fd));
  270. if (ret < 0)
  271. return ret;
  272. }
  273. nlattr_end_nested(&req, nla);
  274. return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
  275. }
  276. int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
  277. {
  278. int old_prog_fd, err;
  279. if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
  280. return libbpf_err(-EINVAL);
  281. old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
  282. if (old_prog_fd)
  283. flags |= XDP_FLAGS_REPLACE;
  284. else
  285. old_prog_fd = -1;
  286. err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
  287. return libbpf_err(err);
  288. }
  289. int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
  290. {
  291. return bpf_xdp_attach(ifindex, -1, flags, opts);
  292. }
  293. static int __dump_link_nlmsg(struct nlmsghdr *nlh,
  294. libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
  295. {
  296. struct nlattr *tb[IFLA_MAX + 1], *attr;
  297. struct ifinfomsg *ifi = NLMSG_DATA(nlh);
  298. int len;
  299. len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
  300. attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
  301. if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
  302. return -LIBBPF_ERRNO__NLPARSE;
  303. return dump_link_nlmsg(cookie, ifi, tb);
  304. }
  305. static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
  306. {
  307. struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
  308. struct xdp_id_md *xdp_id = cookie;
  309. struct ifinfomsg *ifinfo = msg;
  310. int ret;
  311. if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
  312. return 0;
  313. if (!tb[IFLA_XDP])
  314. return 0;
  315. ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
  316. if (ret)
  317. return ret;
  318. if (!xdp_tb[IFLA_XDP_ATTACHED])
  319. return 0;
  320. xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
  321. xdp_tb[IFLA_XDP_ATTACHED]);
  322. if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
  323. return 0;
  324. if (xdp_tb[IFLA_XDP_PROG_ID])
  325. xdp_id->info.prog_id = libbpf_nla_getattr_u32(
  326. xdp_tb[IFLA_XDP_PROG_ID]);
  327. if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
  328. xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
  329. xdp_tb[IFLA_XDP_SKB_PROG_ID]);
  330. if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
  331. xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
  332. xdp_tb[IFLA_XDP_DRV_PROG_ID]);
  333. if (xdp_tb[IFLA_XDP_HW_PROG_ID])
  334. xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
  335. xdp_tb[IFLA_XDP_HW_PROG_ID]);
  336. return 0;
  337. }
  338. static int parse_xdp_features(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
  339. void *cookie)
  340. {
  341. struct genlmsghdr *gnl = NLMSG_DATA(nh);
  342. struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
  343. struct nlattr *tb[NETDEV_CMD_MAX + 1];
  344. struct xdp_features_md *md = cookie;
  345. __u32 ifindex;
  346. libbpf_nla_parse(tb, NETDEV_CMD_MAX, na,
  347. NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
  348. if (!tb[NETDEV_A_DEV_IFINDEX] || !tb[NETDEV_A_DEV_XDP_FEATURES])
  349. return NL_CONT;
  350. ifindex = libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_IFINDEX]);
  351. if (ifindex != md->ifindex)
  352. return NL_CONT;
  353. md->flags = libbpf_nla_getattr_u64(tb[NETDEV_A_DEV_XDP_FEATURES]);
  354. if (tb[NETDEV_A_DEV_XDP_ZC_MAX_SEGS])
  355. md->xdp_zc_max_segs =
  356. libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_XDP_ZC_MAX_SEGS]);
  357. return NL_DONE;
  358. }
  359. int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
  360. {
  361. struct libbpf_nla_req req = {
  362. .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
  363. .nh.nlmsg_type = RTM_GETLINK,
  364. .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
  365. .ifinfo.ifi_family = AF_PACKET,
  366. };
  367. struct xdp_id_md xdp_id = {};
  368. struct xdp_features_md md = {
  369. .ifindex = ifindex,
  370. };
  371. __u16 id;
  372. int err;
  373. if (!OPTS_VALID(opts, bpf_xdp_query_opts))
  374. return libbpf_err(-EINVAL);
  375. if (xdp_flags & ~XDP_FLAGS_MASK)
  376. return libbpf_err(-EINVAL);
  377. /* Check whether the single {HW,DRV,SKB} mode is set */
  378. xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
  379. if (xdp_flags & (xdp_flags - 1))
  380. return libbpf_err(-EINVAL);
  381. xdp_id.ifindex = ifindex;
  382. xdp_id.flags = xdp_flags;
  383. err = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, __dump_link_nlmsg,
  384. get_xdp_info, &xdp_id);
  385. if (err)
  386. return libbpf_err(err);
  387. OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
  388. OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
  389. OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
  390. OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
  391. OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
  392. if (!OPTS_HAS(opts, feature_flags))
  393. return 0;
  394. err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
  395. if (err < 0) {
  396. if (err == -ENOENT) {
  397. opts->feature_flags = 0;
  398. goto skip_feature_flags;
  399. }
  400. return libbpf_err(err);
  401. }
  402. memset(&req, 0, sizeof(req));
  403. req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
  404. req.nh.nlmsg_flags = NLM_F_REQUEST;
  405. req.nh.nlmsg_type = id;
  406. req.gnl.cmd = NETDEV_CMD_DEV_GET;
  407. req.gnl.version = 2;
  408. err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
  409. if (err < 0)
  410. return libbpf_err(err);
  411. err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
  412. parse_xdp_features, NULL, &md);
  413. if (err)
  414. return libbpf_err(err);
  415. OPTS_SET(opts, feature_flags, md.flags);
  416. OPTS_SET(opts, xdp_zc_max_segs, md.xdp_zc_max_segs);
  417. skip_feature_flags:
  418. return 0;
  419. }
  420. int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
  421. {
  422. LIBBPF_OPTS(bpf_xdp_query_opts, opts);
  423. int ret;
  424. ret = bpf_xdp_query(ifindex, flags, &opts);
  425. if (ret)
  426. return libbpf_err(ret);
  427. flags &= XDP_FLAGS_MODES;
  428. if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
  429. *prog_id = opts.prog_id;
  430. else if (flags & XDP_FLAGS_DRV_MODE)
  431. *prog_id = opts.drv_prog_id;
  432. else if (flags & XDP_FLAGS_HW_MODE)
  433. *prog_id = opts.hw_prog_id;
  434. else if (flags & XDP_FLAGS_SKB_MODE)
  435. *prog_id = opts.skb_prog_id;
  436. else
  437. *prog_id = 0;
  438. return 0;
  439. }
  440. typedef int (*qdisc_config_t)(struct libbpf_nla_req *req, const struct bpf_tc_hook *hook);
  441. static int clsact_config(struct libbpf_nla_req *req, const struct bpf_tc_hook *hook)
  442. {
  443. req->tc.tcm_parent = TC_H_CLSACT;
  444. req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
  445. return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
  446. }
  447. static int qdisc_config(struct libbpf_nla_req *req, const struct bpf_tc_hook *hook)
  448. {
  449. const char *qdisc = OPTS_GET(hook, qdisc, NULL);
  450. req->tc.tcm_parent = OPTS_GET(hook, parent, TC_H_ROOT);
  451. req->tc.tcm_handle = OPTS_GET(hook, handle, 0);
  452. return nlattr_add(req, TCA_KIND, qdisc, strlen(qdisc) + 1);
  453. }
  454. static int attach_point_to_config(struct bpf_tc_hook *hook,
  455. qdisc_config_t *config)
  456. {
  457. switch (OPTS_GET(hook, attach_point, 0)) {
  458. case BPF_TC_INGRESS:
  459. case BPF_TC_EGRESS:
  460. case BPF_TC_INGRESS | BPF_TC_EGRESS:
  461. if (OPTS_GET(hook, parent, 0))
  462. return -EINVAL;
  463. *config = &clsact_config;
  464. return 0;
  465. case BPF_TC_CUSTOM:
  466. return -EOPNOTSUPP;
  467. case BPF_TC_QDISC:
  468. *config = &qdisc_config;
  469. return 0;
  470. default:
  471. return -EINVAL;
  472. }
  473. }
  474. static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
  475. __u32 *parent)
  476. {
  477. switch (attach_point) {
  478. case BPF_TC_INGRESS:
  479. case BPF_TC_EGRESS:
  480. if (*parent)
  481. return -EINVAL;
  482. *parent = TC_H_MAKE(TC_H_CLSACT,
  483. attach_point == BPF_TC_INGRESS ?
  484. TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
  485. break;
  486. case BPF_TC_CUSTOM:
  487. if (!*parent)
  488. return -EINVAL;
  489. break;
  490. default:
  491. return -EINVAL;
  492. }
  493. return 0;
  494. }
  495. static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
  496. {
  497. qdisc_config_t config;
  498. int ret;
  499. struct libbpf_nla_req req;
  500. ret = attach_point_to_config(hook, &config);
  501. if (ret < 0)
  502. return ret;
  503. memset(&req, 0, sizeof(req));
  504. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  505. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
  506. req.nh.nlmsg_type = cmd;
  507. req.tc.tcm_family = AF_UNSPEC;
  508. req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
  509. ret = config(&req, hook);
  510. if (ret < 0)
  511. return ret;
  512. return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
  513. }
  514. static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
  515. {
  516. return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
  517. }
  518. static int tc_qdisc_delete(struct bpf_tc_hook *hook)
  519. {
  520. return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
  521. }
  522. int bpf_tc_hook_create(struct bpf_tc_hook *hook)
  523. {
  524. int ret;
  525. if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
  526. OPTS_GET(hook, ifindex, 0) <= 0)
  527. return libbpf_err(-EINVAL);
  528. ret = tc_qdisc_create_excl(hook);
  529. return libbpf_err(ret);
  530. }
  531. static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
  532. const struct bpf_tc_opts *opts,
  533. const bool flush);
  534. int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
  535. {
  536. if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
  537. OPTS_GET(hook, ifindex, 0) <= 0)
  538. return libbpf_err(-EINVAL);
  539. switch (OPTS_GET(hook, attach_point, 0)) {
  540. case BPF_TC_INGRESS:
  541. case BPF_TC_EGRESS:
  542. return libbpf_err(__bpf_tc_detach(hook, NULL, true));
  543. case BPF_TC_QDISC:
  544. case BPF_TC_INGRESS | BPF_TC_EGRESS:
  545. return libbpf_err(tc_qdisc_delete(hook));
  546. case BPF_TC_CUSTOM:
  547. return libbpf_err(-EOPNOTSUPP);
  548. default:
  549. return libbpf_err(-EINVAL);
  550. }
  551. }
  552. struct bpf_cb_ctx {
  553. struct bpf_tc_opts *opts;
  554. bool processed;
  555. };
  556. static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
  557. bool unicast)
  558. {
  559. struct nlattr *tbb[TCA_BPF_MAX + 1];
  560. struct bpf_cb_ctx *info = cookie;
  561. if (!info || !info->opts)
  562. return -EINVAL;
  563. if (unicast && info->processed)
  564. return -EINVAL;
  565. if (!tb[TCA_OPTIONS])
  566. return NL_CONT;
  567. libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
  568. if (!tbb[TCA_BPF_ID])
  569. return -EINVAL;
  570. OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
  571. OPTS_SET(info->opts, handle, tc->tcm_handle);
  572. OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
  573. info->processed = true;
  574. return unicast ? NL_NEXT : NL_DONE;
  575. }
  576. static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
  577. void *cookie)
  578. {
  579. struct tcmsg *tc = NLMSG_DATA(nh);
  580. struct nlattr *tb[TCA_MAX + 1];
  581. libbpf_nla_parse(tb, TCA_MAX,
  582. (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
  583. NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
  584. if (!tb[TCA_KIND])
  585. return NL_CONT;
  586. return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
  587. }
  588. static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
  589. {
  590. struct bpf_prog_info info;
  591. __u32 info_len = sizeof(info);
  592. char name[256];
  593. int len, ret;
  594. memset(&info, 0, info_len);
  595. ret = bpf_prog_get_info_by_fd(fd, &info, &info_len);
  596. if (ret < 0)
  597. return ret;
  598. ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
  599. if (ret < 0)
  600. return ret;
  601. len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
  602. if (len < 0)
  603. return -errno;
  604. if (len >= sizeof(name))
  605. return -ENAMETOOLONG;
  606. return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
  607. }
  608. int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
  609. {
  610. __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
  611. int ret, ifindex, attach_point, prog_fd;
  612. struct bpf_cb_ctx info = {};
  613. struct libbpf_nla_req req;
  614. struct nlattr *nla;
  615. if (!hook || !opts ||
  616. !OPTS_VALID(hook, bpf_tc_hook) ||
  617. !OPTS_VALID(opts, bpf_tc_opts))
  618. return libbpf_err(-EINVAL);
  619. ifindex = OPTS_GET(hook, ifindex, 0);
  620. parent = OPTS_GET(hook, parent, 0);
  621. attach_point = OPTS_GET(hook, attach_point, 0);
  622. handle = OPTS_GET(opts, handle, 0);
  623. priority = OPTS_GET(opts, priority, 0);
  624. prog_fd = OPTS_GET(opts, prog_fd, 0);
  625. prog_id = OPTS_GET(opts, prog_id, 0);
  626. flags = OPTS_GET(opts, flags, 0);
  627. if (ifindex <= 0 || !prog_fd || prog_id)
  628. return libbpf_err(-EINVAL);
  629. if (priority > UINT16_MAX)
  630. return libbpf_err(-EINVAL);
  631. if (flags & ~BPF_TC_F_REPLACE)
  632. return libbpf_err(-EINVAL);
  633. flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
  634. protocol = ETH_P_ALL;
  635. memset(&req, 0, sizeof(req));
  636. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  637. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
  638. NLM_F_ECHO | flags;
  639. req.nh.nlmsg_type = RTM_NEWTFILTER;
  640. req.tc.tcm_family = AF_UNSPEC;
  641. req.tc.tcm_ifindex = ifindex;
  642. req.tc.tcm_handle = handle;
  643. req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
  644. ret = tc_get_tcm_parent(attach_point, &parent);
  645. if (ret < 0)
  646. return libbpf_err(ret);
  647. req.tc.tcm_parent = parent;
  648. ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
  649. if (ret < 0)
  650. return libbpf_err(ret);
  651. nla = nlattr_begin_nested(&req, TCA_OPTIONS);
  652. if (!nla)
  653. return libbpf_err(-EMSGSIZE);
  654. ret = tc_add_fd_and_name(&req, prog_fd);
  655. if (ret < 0)
  656. return libbpf_err(ret);
  657. bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
  658. ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
  659. if (ret < 0)
  660. return libbpf_err(ret);
  661. nlattr_end_nested(&req, nla);
  662. info.opts = opts;
  663. ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL,
  664. &info);
  665. if (ret < 0)
  666. return libbpf_err(ret);
  667. if (!info.processed)
  668. return libbpf_err(-ENOENT);
  669. return ret;
  670. }
  671. static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
  672. const struct bpf_tc_opts *opts,
  673. const bool flush)
  674. {
  675. __u32 protocol = 0, handle, priority, parent, prog_id, flags;
  676. int ret, ifindex, attach_point, prog_fd;
  677. struct libbpf_nla_req req;
  678. if (!hook ||
  679. !OPTS_VALID(hook, bpf_tc_hook) ||
  680. !OPTS_VALID(opts, bpf_tc_opts))
  681. return -EINVAL;
  682. ifindex = OPTS_GET(hook, ifindex, 0);
  683. parent = OPTS_GET(hook, parent, 0);
  684. attach_point = OPTS_GET(hook, attach_point, 0);
  685. handle = OPTS_GET(opts, handle, 0);
  686. priority = OPTS_GET(opts, priority, 0);
  687. prog_fd = OPTS_GET(opts, prog_fd, 0);
  688. prog_id = OPTS_GET(opts, prog_id, 0);
  689. flags = OPTS_GET(opts, flags, 0);
  690. if (ifindex <= 0 || flags || prog_fd || prog_id)
  691. return -EINVAL;
  692. if (priority > UINT16_MAX)
  693. return -EINVAL;
  694. if (!flush) {
  695. if (!handle || !priority)
  696. return -EINVAL;
  697. protocol = ETH_P_ALL;
  698. } else {
  699. if (handle || priority)
  700. return -EINVAL;
  701. }
  702. memset(&req, 0, sizeof(req));
  703. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  704. req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  705. req.nh.nlmsg_type = RTM_DELTFILTER;
  706. req.tc.tcm_family = AF_UNSPEC;
  707. req.tc.tcm_ifindex = ifindex;
  708. if (!flush) {
  709. req.tc.tcm_handle = handle;
  710. req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
  711. }
  712. ret = tc_get_tcm_parent(attach_point, &parent);
  713. if (ret < 0)
  714. return ret;
  715. req.tc.tcm_parent = parent;
  716. if (!flush) {
  717. ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
  718. if (ret < 0)
  719. return ret;
  720. }
  721. return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
  722. }
  723. int bpf_tc_detach(const struct bpf_tc_hook *hook,
  724. const struct bpf_tc_opts *opts)
  725. {
  726. int ret;
  727. if (!opts)
  728. return libbpf_err(-EINVAL);
  729. ret = __bpf_tc_detach(hook, opts, false);
  730. return libbpf_err(ret);
  731. }
  732. int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
  733. {
  734. __u32 protocol, handle, priority, parent, prog_id, flags;
  735. int ret, ifindex, attach_point, prog_fd;
  736. struct bpf_cb_ctx info = {};
  737. struct libbpf_nla_req req;
  738. if (!hook || !opts ||
  739. !OPTS_VALID(hook, bpf_tc_hook) ||
  740. !OPTS_VALID(opts, bpf_tc_opts))
  741. return libbpf_err(-EINVAL);
  742. ifindex = OPTS_GET(hook, ifindex, 0);
  743. parent = OPTS_GET(hook, parent, 0);
  744. attach_point = OPTS_GET(hook, attach_point, 0);
  745. handle = OPTS_GET(opts, handle, 0);
  746. priority = OPTS_GET(opts, priority, 0);
  747. prog_fd = OPTS_GET(opts, prog_fd, 0);
  748. prog_id = OPTS_GET(opts, prog_id, 0);
  749. flags = OPTS_GET(opts, flags, 0);
  750. if (ifindex <= 0 || flags || prog_fd || prog_id ||
  751. !handle || !priority)
  752. return libbpf_err(-EINVAL);
  753. if (priority > UINT16_MAX)
  754. return libbpf_err(-EINVAL);
  755. protocol = ETH_P_ALL;
  756. memset(&req, 0, sizeof(req));
  757. req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
  758. req.nh.nlmsg_flags = NLM_F_REQUEST;
  759. req.nh.nlmsg_type = RTM_GETTFILTER;
  760. req.tc.tcm_family = AF_UNSPEC;
  761. req.tc.tcm_ifindex = ifindex;
  762. req.tc.tcm_handle = handle;
  763. req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
  764. ret = tc_get_tcm_parent(attach_point, &parent);
  765. if (ret < 0)
  766. return libbpf_err(ret);
  767. req.tc.tcm_parent = parent;
  768. ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
  769. if (ret < 0)
  770. return libbpf_err(ret);
  771. info.opts = opts;
  772. ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL,
  773. &info);
  774. if (ret < 0)
  775. return libbpf_err(ret);
  776. if (!info.processed)
  777. return libbpf_err(-ENOENT);
  778. return ret;
  779. }