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