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 /* 0: no done, 1: done found, 2: extack found, -1: error */ 35 static int nl_get_extack(char *buf, size_t n, struct ext_ack *ea) 36 { 37 const struct nlmsghdr *nlh; 38 const struct nlattr *attr; 39 ssize_t rem; 40 41 for (rem = n; rem > 0; NLMSG_NEXT(nlh, rem)) { 42 nlh = (struct nlmsghdr *)&buf[n - rem]; 43 if (!NLMSG_OK(nlh, rem)) 44 return -1; 45 46 if (nlh->nlmsg_type != NLMSG_DONE) 47 continue; 48 49 ea->err = -*(int *)NLMSG_DATA(nlh); 50 51 if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) 52 return 1; 53 54 ynl_attr_for_each(attr, nlh, sizeof(int)) { 55 switch (ynl_attr_type(attr)) { 56 case NLMSGERR_ATTR_OFFS: 57 ea->attr_offs = ynl_attr_get_u32(attr); 58 break; 59 case NLMSGERR_ATTR_MISS_TYPE: 60 ea->miss_type = ynl_attr_get_u32(attr); 61 break; 62 case NLMSGERR_ATTR_MISS_NEST: 63 ea->miss_nest = ynl_attr_get_u32(attr); 64 break; 65 case NLMSGERR_ATTR_MSG: 66 ea->str = ynl_attr_get_str(attr); 67 break; 68 } 69 } 70 71 return 2; 72 } 73 74 return 0; 75 } 76 77 static const struct { 78 struct nlmsghdr nlhdr; 79 struct ndmsg ndm; 80 struct nlattr ahdr; 81 __u32 val; 82 } dump_neigh_bad = { 83 .nlhdr = { 84 .nlmsg_len = sizeof(dump_neigh_bad), 85 .nlmsg_type = RTM_GETNEIGH, 86 .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 87 .nlmsg_seq = 1, 88 }, 89 .ndm = { 90 .ndm_family = 123, 91 }, 92 .ahdr = { 93 .nla_len = 4 + 4, 94 .nla_type = NDA_FLAGS_EXT, 95 }, 96 .val = -1, // should fail MASK validation 97 }; 98 99 TEST(dump_extack) 100 { 101 int netlink_sock; 102 char buf[8192]; 103 int one = 1; 104 int i, cnt; 105 ssize_t n; 106 107 netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 108 ASSERT_GE(netlink_sock, 0); 109 110 n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_CAP_ACK, 111 &one, sizeof(one)); 112 ASSERT_EQ(n, 0); 113 n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_EXT_ACK, 114 &one, sizeof(one)); 115 ASSERT_EQ(n, 0); 116 n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_GET_STRICT_CHK, 117 &one, sizeof(one)); 118 ASSERT_EQ(n, 0); 119 120 /* Dump so many times we fill up the buffer */ 121 cnt = 64; 122 for (i = 0; i < cnt; i++) { 123 n = send(netlink_sock, &dump_neigh_bad, 124 sizeof(dump_neigh_bad), 0); 125 ASSERT_EQ(n, sizeof(dump_neigh_bad)); 126 } 127 128 /* Read out the ENOBUFS */ 129 n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT); 130 EXPECT_EQ(n, -1); 131 EXPECT_EQ(errno, ENOBUFS); 132 133 for (i = 0; i < cnt; i++) { 134 struct ext_ack ea = {}; 135 136 n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT); 137 if (n < 0) { 138 ASSERT_GE(i, 10); 139 break; 140 } 141 ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr)); 142 143 EXPECT_EQ(nl_get_extack(buf, n, &ea), 2); 144 EXPECT_EQ(ea.attr_offs, 145 sizeof(struct nlmsghdr) + sizeof(struct ndmsg)); 146 } 147 } 148 149 static const struct { 150 struct nlmsghdr nlhdr; 151 struct genlmsghdr genlhdr; 152 struct nlattr ahdr; 153 __u16 val; 154 __u16 pad; 155 } dump_policies = { 156 .nlhdr = { 157 .nlmsg_len = sizeof(dump_policies), 158 .nlmsg_type = GENL_ID_CTRL, 159 .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 160 .nlmsg_seq = 1, 161 }, 162 .genlhdr = { 163 .cmd = CTRL_CMD_GETPOLICY, 164 .version = 2, 165 }, 166 .ahdr = { 167 .nla_len = 6, 168 .nla_type = CTRL_ATTR_FAMILY_ID, 169 }, 170 .val = GENL_ID_CTRL, 171 .pad = 0, 172 }; 173 174 // Sanity check for the test itself, make sure the dump doesn't fit in one msg 175 TEST(test_sanity) 176 { 177 int netlink_sock; 178 char buf[8192]; 179 ssize_t n; 180 181 netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); 182 ASSERT_GE(netlink_sock, 0); 183 184 n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0); 185 ASSERT_EQ(n, sizeof(dump_policies)); 186 187 n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT); 188 ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr)); 189 190 n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT); 191 ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr)); 192 193 close(netlink_sock); 194 } 195 196 TEST(close_in_progress) 197 { 198 int netlink_sock; 199 ssize_t n; 200 201 netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); 202 ASSERT_GE(netlink_sock, 0); 203 204 n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0); 205 ASSERT_EQ(n, sizeof(dump_policies)); 206 207 close(netlink_sock); 208 } 209 210 TEST(close_with_ref) 211 { 212 char cookie[NOTIFY_COOKIE_LEN] = {}; 213 int netlink_sock, mq_fd; 214 struct sigevent sigev; 215 ssize_t n; 216 217 netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); 218 ASSERT_GE(netlink_sock, 0); 219 220 n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0); 221 ASSERT_EQ(n, sizeof(dump_policies)); 222 223 mq_fd = syscall(__NR_mq_open, "sed", O_CREAT | O_WRONLY, 0600, 0); 224 ASSERT_GE(mq_fd, 0); 225 226 memset(&sigev, 0, sizeof(sigev)); 227 sigev.sigev_notify = SIGEV_THREAD; 228 sigev.sigev_value.sival_ptr = cookie; 229 sigev.sigev_signo = netlink_sock; 230 231 syscall(__NR_mq_notify, mq_fd, &sigev); 232 233 close(netlink_sock); 234 235 // give mqueue time to fire 236 usleep(100 * 1000); 237 } 238 239 TEST_HARNESS_MAIN 240