netlink-dumps.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <sys/socket.h>
  7. #include <sys/stat.h>
  8. #include <sys/syscall.h>
  9. #include <sys/types.h>
  10. #include <unistd.h>
  11. #include <linux/genetlink.h>
  12. #include <linux/neighbour.h>
  13. #include <linux/netdevice.h>
  14. #include <linux/netlink.h>
  15. #include <linux/mqueue.h>
  16. #include <linux/rtnetlink.h>
  17. #include "kselftest_harness.h"
  18. #include <ynl.h>
  19. struct ext_ack {
  20. int err;
  21. __u32 attr_offs;
  22. __u32 miss_type;
  23. __u32 miss_nest;
  24. const char *str;
  25. };
  26. enum get_ea_ret {
  27. ERROR = -1,
  28. NO_CTRL = 0,
  29. FOUND_DONE,
  30. FOUND_ERR,
  31. FOUND_EXTACK,
  32. };
  33. static enum get_ea_ret
  34. nl_get_extack(char *buf, size_t n, struct ext_ack *ea)
  35. {
  36. enum get_ea_ret ret = NO_CTRL;
  37. const struct nlmsghdr *nlh;
  38. const struct nlattr *attr;
  39. ssize_t rem;
  40. for (rem = n; rem > 0; NLMSG_NEXT(nlh, rem)) {
  41. nlh = (struct nlmsghdr *)&buf[n - rem];
  42. if (!NLMSG_OK(nlh, rem))
  43. return ERROR;
  44. if (nlh->nlmsg_type == NLMSG_ERROR)
  45. ret = FOUND_ERR;
  46. else if (nlh->nlmsg_type == NLMSG_DONE)
  47. ret = FOUND_DONE;
  48. else
  49. continue;
  50. ea->err = -*(int *)NLMSG_DATA(nlh);
  51. if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
  52. return ret;
  53. ynl_attr_for_each(attr, nlh, sizeof(int)) {
  54. switch (ynl_attr_type(attr)) {
  55. case NLMSGERR_ATTR_OFFS:
  56. ea->attr_offs = ynl_attr_get_u32(attr);
  57. break;
  58. case NLMSGERR_ATTR_MISS_TYPE:
  59. ea->miss_type = ynl_attr_get_u32(attr);
  60. break;
  61. case NLMSGERR_ATTR_MISS_NEST:
  62. ea->miss_nest = ynl_attr_get_u32(attr);
  63. break;
  64. case NLMSGERR_ATTR_MSG:
  65. ea->str = ynl_attr_get_str(attr);
  66. break;
  67. }
  68. }
  69. return FOUND_EXTACK;
  70. }
  71. return ret;
  72. }
  73. static const struct {
  74. struct nlmsghdr nlhdr;
  75. struct ndmsg ndm;
  76. struct nlattr ahdr;
  77. __u32 val;
  78. } dump_neigh_bad = {
  79. .nlhdr = {
  80. .nlmsg_len = sizeof(dump_neigh_bad),
  81. .nlmsg_type = RTM_GETNEIGH,
  82. .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
  83. .nlmsg_seq = 1,
  84. },
  85. .ndm = {
  86. .ndm_family = 123,
  87. },
  88. .ahdr = {
  89. .nla_len = 4 + 4,
  90. .nla_type = NDA_FLAGS_EXT,
  91. },
  92. .val = -1, // should fail MASK validation
  93. };
  94. TEST(dump_extack)
  95. {
  96. int netlink_sock;
  97. int i, cnt, ret;
  98. char buf[8192];
  99. int one = 1;
  100. ssize_t n;
  101. netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  102. ASSERT_GE(netlink_sock, 0);
  103. n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_CAP_ACK,
  104. &one, sizeof(one));
  105. ASSERT_EQ(n, 0);
  106. n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_EXT_ACK,
  107. &one, sizeof(one));
  108. ASSERT_EQ(n, 0);
  109. n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_GET_STRICT_CHK,
  110. &one, sizeof(one));
  111. ASSERT_EQ(n, 0);
  112. /* Dump so many times we fill up the buffer */
  113. cnt = 80;
  114. for (i = 0; i < cnt; i++) {
  115. n = send(netlink_sock, &dump_neigh_bad,
  116. sizeof(dump_neigh_bad), 0);
  117. ASSERT_EQ(n, sizeof(dump_neigh_bad));
  118. }
  119. /* Read out the ENOBUFS */
  120. n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
  121. EXPECT_EQ(n, -1);
  122. EXPECT_EQ(errno, ENOBUFS);
  123. ret = NO_CTRL;
  124. for (i = 0; i < cnt; i++) {
  125. struct ext_ack ea = {};
  126. n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
  127. if (n < 0) {
  128. ASSERT_GE(i, 10);
  129. break;
  130. }
  131. ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
  132. ret = nl_get_extack(buf, n, &ea);
  133. /* Once we fill the buffer we'll see one ENOBUFS followed
  134. * by a number of EBUSYs. Then the last recv() will finally
  135. * trigger and complete the dump.
  136. */
  137. if (ret == FOUND_ERR && (ea.err == ENOBUFS || ea.err == EBUSY))
  138. continue;
  139. EXPECT_EQ(ret, FOUND_EXTACK);
  140. EXPECT_EQ(ea.err, EINVAL);
  141. EXPECT_EQ(ea.attr_offs,
  142. sizeof(struct nlmsghdr) + sizeof(struct ndmsg));
  143. }
  144. /* Make sure last message was a full DONE+extack */
  145. EXPECT_EQ(ret, FOUND_EXTACK);
  146. }
  147. static const struct {
  148. struct nlmsghdr nlhdr;
  149. struct genlmsghdr genlhdr;
  150. struct nlattr ahdr;
  151. __u16 val;
  152. __u16 pad;
  153. } dump_policies = {
  154. .nlhdr = {
  155. .nlmsg_len = sizeof(dump_policies),
  156. .nlmsg_type = GENL_ID_CTRL,
  157. .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
  158. .nlmsg_seq = 1,
  159. },
  160. .genlhdr = {
  161. .cmd = CTRL_CMD_GETPOLICY,
  162. .version = 2,
  163. },
  164. .ahdr = {
  165. .nla_len = 6,
  166. .nla_type = CTRL_ATTR_FAMILY_ID,
  167. },
  168. .val = GENL_ID_CTRL,
  169. .pad = 0,
  170. };
  171. // Sanity check for the test itself, make sure the dump doesn't fit in one msg
  172. TEST(test_sanity)
  173. {
  174. int netlink_sock;
  175. char buf[8192];
  176. ssize_t n;
  177. netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
  178. ASSERT_GE(netlink_sock, 0);
  179. n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0);
  180. ASSERT_EQ(n, sizeof(dump_policies));
  181. n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
  182. ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
  183. n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
  184. ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
  185. close(netlink_sock);
  186. }
  187. TEST(close_in_progress)
  188. {
  189. int netlink_sock;
  190. ssize_t n;
  191. netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
  192. ASSERT_GE(netlink_sock, 0);
  193. n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0);
  194. ASSERT_EQ(n, sizeof(dump_policies));
  195. close(netlink_sock);
  196. }
  197. TEST(close_with_ref)
  198. {
  199. char cookie[NOTIFY_COOKIE_LEN] = {};
  200. int netlink_sock, mq_fd;
  201. struct sigevent sigev;
  202. ssize_t n;
  203. netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
  204. ASSERT_GE(netlink_sock, 0);
  205. n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0);
  206. ASSERT_EQ(n, sizeof(dump_policies));
  207. mq_fd = syscall(__NR_mq_open, "sed", O_CREAT | O_WRONLY, 0600, 0);
  208. ASSERT_GE(mq_fd, 0);
  209. memset(&sigev, 0, sizeof(sigev));
  210. sigev.sigev_notify = SIGEV_THREAD;
  211. sigev.sigev_value.sival_ptr = cookie;
  212. sigev.sigev_signo = netlink_sock;
  213. syscall(__NR_mq_notify, mq_fd, &sigev);
  214. close(netlink_sock);
  215. // give mqueue time to fire
  216. usleep(100 * 1000);
  217. }
  218. TEST_HARNESS_MAIN