tcx.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright (c) 2023 Isovalent */
  3. #include <linux/bpf.h>
  4. #include <linux/bpf_mprog.h>
  5. #include <linux/netdevice.h>
  6. #include <net/tcx.h>
  7. int tcx_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
  8. {
  9. bool created, ingress = attr->attach_type == BPF_TCX_INGRESS;
  10. struct net *net = current->nsproxy->net_ns;
  11. struct bpf_mprog_entry *entry, *entry_new;
  12. struct bpf_prog *replace_prog = NULL;
  13. struct net_device *dev;
  14. int ret;
  15. rtnl_lock();
  16. dev = __dev_get_by_index(net, attr->target_ifindex);
  17. if (!dev) {
  18. ret = -ENODEV;
  19. goto out;
  20. }
  21. if (attr->attach_flags & BPF_F_REPLACE) {
  22. replace_prog = bpf_prog_get_type(attr->replace_bpf_fd,
  23. prog->type);
  24. if (IS_ERR(replace_prog)) {
  25. ret = PTR_ERR(replace_prog);
  26. replace_prog = NULL;
  27. goto out;
  28. }
  29. }
  30. entry = tcx_entry_fetch_or_create(dev, ingress, &created);
  31. if (!entry) {
  32. ret = -ENOMEM;
  33. goto out;
  34. }
  35. ret = bpf_mprog_attach(entry, &entry_new, prog, NULL, replace_prog,
  36. attr->attach_flags, attr->relative_fd,
  37. attr->expected_revision);
  38. if (!ret) {
  39. if (entry != entry_new) {
  40. tcx_entry_update(dev, entry_new, ingress);
  41. tcx_entry_sync();
  42. tcx_skeys_inc(ingress);
  43. }
  44. bpf_mprog_commit(entry);
  45. } else if (created) {
  46. tcx_entry_free(entry);
  47. }
  48. out:
  49. if (replace_prog)
  50. bpf_prog_put(replace_prog);
  51. rtnl_unlock();
  52. return ret;
  53. }
  54. int tcx_prog_detach(const union bpf_attr *attr, struct bpf_prog *prog)
  55. {
  56. bool ingress = attr->attach_type == BPF_TCX_INGRESS;
  57. struct net *net = current->nsproxy->net_ns;
  58. struct bpf_mprog_entry *entry, *entry_new;
  59. struct net_device *dev;
  60. int ret;
  61. rtnl_lock();
  62. dev = __dev_get_by_index(net, attr->target_ifindex);
  63. if (!dev) {
  64. ret = -ENODEV;
  65. goto out;
  66. }
  67. entry = tcx_entry_fetch(dev, ingress);
  68. if (!entry) {
  69. ret = -ENOENT;
  70. goto out;
  71. }
  72. ret = bpf_mprog_detach(entry, &entry_new, prog, NULL, attr->attach_flags,
  73. attr->relative_fd, attr->expected_revision);
  74. if (!ret) {
  75. if (!tcx_entry_is_active(entry_new))
  76. entry_new = NULL;
  77. tcx_entry_update(dev, entry_new, ingress);
  78. tcx_entry_sync();
  79. tcx_skeys_dec(ingress);
  80. bpf_mprog_commit(entry);
  81. if (!entry_new)
  82. tcx_entry_free(entry);
  83. }
  84. out:
  85. rtnl_unlock();
  86. return ret;
  87. }
  88. void tcx_uninstall(struct net_device *dev, bool ingress)
  89. {
  90. struct bpf_mprog_entry *entry, *entry_new = NULL;
  91. struct bpf_tuple tuple = {};
  92. struct bpf_mprog_fp *fp;
  93. struct bpf_mprog_cp *cp;
  94. bool active;
  95. entry = tcx_entry_fetch(dev, ingress);
  96. if (!entry)
  97. return;
  98. active = tcx_entry(entry)->miniq_active;
  99. if (active)
  100. bpf_mprog_clear_all(entry, &entry_new);
  101. tcx_entry_update(dev, entry_new, ingress);
  102. tcx_entry_sync();
  103. bpf_mprog_foreach_tuple(entry, fp, cp, tuple) {
  104. if (tuple.link)
  105. tcx_link(tuple.link)->dev = NULL;
  106. else
  107. bpf_prog_put(tuple.prog);
  108. tcx_skeys_dec(ingress);
  109. }
  110. if (!active)
  111. tcx_entry_free(entry);
  112. }
  113. int tcx_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
  114. {
  115. bool ingress = attr->query.attach_type == BPF_TCX_INGRESS;
  116. struct net *net = current->nsproxy->net_ns;
  117. struct net_device *dev;
  118. int ret;
  119. rtnl_lock();
  120. dev = __dev_get_by_index(net, attr->query.target_ifindex);
  121. if (!dev) {
  122. ret = -ENODEV;
  123. goto out;
  124. }
  125. ret = bpf_mprog_query(attr, uattr, tcx_entry_fetch(dev, ingress));
  126. out:
  127. rtnl_unlock();
  128. return ret;
  129. }
  130. static int tcx_link_prog_attach(struct bpf_link *link, u32 flags, u32 id_or_fd,
  131. u64 revision)
  132. {
  133. struct tcx_link *tcx = tcx_link(link);
  134. bool created, ingress = tcx->location == BPF_TCX_INGRESS;
  135. struct bpf_mprog_entry *entry, *entry_new;
  136. struct net_device *dev = tcx->dev;
  137. int ret;
  138. ASSERT_RTNL();
  139. entry = tcx_entry_fetch_or_create(dev, ingress, &created);
  140. if (!entry)
  141. return -ENOMEM;
  142. ret = bpf_mprog_attach(entry, &entry_new, link->prog, link, NULL, flags,
  143. id_or_fd, revision);
  144. if (!ret) {
  145. if (entry != entry_new) {
  146. tcx_entry_update(dev, entry_new, ingress);
  147. tcx_entry_sync();
  148. tcx_skeys_inc(ingress);
  149. }
  150. bpf_mprog_commit(entry);
  151. } else if (created) {
  152. tcx_entry_free(entry);
  153. }
  154. return ret;
  155. }
  156. static void tcx_link_release(struct bpf_link *link)
  157. {
  158. struct tcx_link *tcx = tcx_link(link);
  159. bool ingress = tcx->location == BPF_TCX_INGRESS;
  160. struct bpf_mprog_entry *entry, *entry_new;
  161. struct net_device *dev;
  162. int ret = 0;
  163. rtnl_lock();
  164. dev = tcx->dev;
  165. if (!dev)
  166. goto out;
  167. entry = tcx_entry_fetch(dev, ingress);
  168. if (!entry) {
  169. ret = -ENOENT;
  170. goto out;
  171. }
  172. ret = bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0);
  173. if (!ret) {
  174. if (!tcx_entry_is_active(entry_new))
  175. entry_new = NULL;
  176. tcx_entry_update(dev, entry_new, ingress);
  177. tcx_entry_sync();
  178. tcx_skeys_dec(ingress);
  179. bpf_mprog_commit(entry);
  180. if (!entry_new)
  181. tcx_entry_free(entry);
  182. tcx->dev = NULL;
  183. }
  184. out:
  185. WARN_ON_ONCE(ret);
  186. rtnl_unlock();
  187. }
  188. static int tcx_link_update(struct bpf_link *link, struct bpf_prog *nprog,
  189. struct bpf_prog *oprog)
  190. {
  191. struct tcx_link *tcx = tcx_link(link);
  192. bool ingress = tcx->location == BPF_TCX_INGRESS;
  193. struct bpf_mprog_entry *entry, *entry_new;
  194. struct net_device *dev;
  195. int ret = 0;
  196. rtnl_lock();
  197. dev = tcx->dev;
  198. if (!dev) {
  199. ret = -ENOLINK;
  200. goto out;
  201. }
  202. if (oprog && link->prog != oprog) {
  203. ret = -EPERM;
  204. goto out;
  205. }
  206. oprog = link->prog;
  207. if (oprog == nprog) {
  208. bpf_prog_put(nprog);
  209. goto out;
  210. }
  211. entry = tcx_entry_fetch(dev, ingress);
  212. if (!entry) {
  213. ret = -ENOENT;
  214. goto out;
  215. }
  216. ret = bpf_mprog_attach(entry, &entry_new, nprog, link, oprog,
  217. BPF_F_REPLACE | BPF_F_ID,
  218. link->prog->aux->id, 0);
  219. if (!ret) {
  220. WARN_ON_ONCE(entry != entry_new);
  221. oprog = xchg(&link->prog, nprog);
  222. bpf_prog_put(oprog);
  223. bpf_mprog_commit(entry);
  224. }
  225. out:
  226. rtnl_unlock();
  227. return ret;
  228. }
  229. static void tcx_link_dealloc(struct bpf_link *link)
  230. {
  231. kfree(tcx_link(link));
  232. }
  233. static void tcx_link_fdinfo(const struct bpf_link *link, struct seq_file *seq)
  234. {
  235. const struct tcx_link *tcx = tcx_link(link);
  236. u32 ifindex = 0;
  237. rtnl_lock();
  238. if (tcx->dev)
  239. ifindex = tcx->dev->ifindex;
  240. rtnl_unlock();
  241. seq_printf(seq, "ifindex:\t%u\n", ifindex);
  242. seq_printf(seq, "attach_type:\t%u (%s)\n",
  243. tcx->location,
  244. tcx->location == BPF_TCX_INGRESS ? "ingress" : "egress");
  245. }
  246. static int tcx_link_fill_info(const struct bpf_link *link,
  247. struct bpf_link_info *info)
  248. {
  249. const struct tcx_link *tcx = tcx_link(link);
  250. u32 ifindex = 0;
  251. rtnl_lock();
  252. if (tcx->dev)
  253. ifindex = tcx->dev->ifindex;
  254. rtnl_unlock();
  255. info->tcx.ifindex = ifindex;
  256. info->tcx.attach_type = tcx->location;
  257. return 0;
  258. }
  259. static int tcx_link_detach(struct bpf_link *link)
  260. {
  261. tcx_link_release(link);
  262. return 0;
  263. }
  264. static const struct bpf_link_ops tcx_link_lops = {
  265. .release = tcx_link_release,
  266. .detach = tcx_link_detach,
  267. .dealloc = tcx_link_dealloc,
  268. .update_prog = tcx_link_update,
  269. .show_fdinfo = tcx_link_fdinfo,
  270. .fill_link_info = tcx_link_fill_info,
  271. };
  272. static int tcx_link_init(struct tcx_link *tcx,
  273. struct bpf_link_primer *link_primer,
  274. const union bpf_attr *attr,
  275. struct net_device *dev,
  276. struct bpf_prog *prog)
  277. {
  278. bpf_link_init(&tcx->link, BPF_LINK_TYPE_TCX, &tcx_link_lops, prog);
  279. tcx->location = attr->link_create.attach_type;
  280. tcx->dev = dev;
  281. return bpf_link_prime(&tcx->link, link_primer);
  282. }
  283. int tcx_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
  284. {
  285. struct net *net = current->nsproxy->net_ns;
  286. struct bpf_link_primer link_primer;
  287. struct net_device *dev;
  288. struct tcx_link *tcx;
  289. int ret;
  290. rtnl_lock();
  291. dev = __dev_get_by_index(net, attr->link_create.target_ifindex);
  292. if (!dev) {
  293. ret = -ENODEV;
  294. goto out;
  295. }
  296. tcx = kzalloc(sizeof(*tcx), GFP_USER);
  297. if (!tcx) {
  298. ret = -ENOMEM;
  299. goto out;
  300. }
  301. ret = tcx_link_init(tcx, &link_primer, attr, dev, prog);
  302. if (ret) {
  303. kfree(tcx);
  304. goto out;
  305. }
  306. ret = tcx_link_prog_attach(&tcx->link, attr->link_create.flags,
  307. attr->link_create.tcx.relative_fd,
  308. attr->link_create.tcx.expected_revision);
  309. if (ret) {
  310. tcx->dev = NULL;
  311. bpf_link_cleanup(&link_primer);
  312. goto out;
  313. }
  314. ret = bpf_link_settle(&link_primer);
  315. out:
  316. rtnl_unlock();
  317. return ret;
  318. }