1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 /* Copyright (c) 2018 Facebook */ 3 4 #include <stdlib.h> 5 #include <memory.h> 6 #include <unistd.h> 7 #include <linux/bpf.h> 8 #include <linux/rtnetlink.h> 9 #include <sys/socket.h> 10 #include <errno.h> 11 #include <time.h> 12 13 #include "bpf.h" 14 #include "libbpf.h" 15 #include "libbpf_internal.h" 16 #include "nlattr.h" 17 18 #ifndef SOL_NETLINK 19 #define SOL_NETLINK 270 20 #endif 21 22 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); 23 24 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 25 void *cookie); 26 27 struct xdp_id_md { 28 int ifindex; 29 __u32 flags; 30 struct xdp_link_info info; 31 }; 32 33 static int libbpf_netlink_open(__u32 *nl_pid) 34 { 35 struct sockaddr_nl sa; 36 socklen_t addrlen; 37 int one = 1, ret; 38 int sock; 39 40 memset(&sa, 0, sizeof(sa)); 41 sa.nl_family = AF_NETLINK; 42 43 sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); 44 if (sock < 0) 45 return -errno; 46 47 if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 48 &one, sizeof(one)) < 0) { 49 pr_warn("Netlink error reporting not supported\n"); 50 } 51 52 if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 53 ret = -errno; 54 goto cleanup; 55 } 56 57 addrlen = sizeof(sa); 58 if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 59 ret = -errno; 60 goto cleanup; 61 } 62 63 if (addrlen != sizeof(sa)) { 64 ret = -LIBBPF_ERRNO__INTERNAL; 65 goto cleanup; 66 } 67 68 *nl_pid = sa.nl_pid; 69 return sock; 70 71 cleanup: 72 close(sock); 73 return ret; 74 } 75 76 static void libbpf_netlink_close(int sock) 77 { 78 close(sock); 79 } 80 81 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq, 82 __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, 83 void *cookie) 84 { 85 bool multipart = true; 86 struct nlmsgerr *err; 87 struct nlmsghdr *nh; 88 char buf[4096]; 89 int len, ret; 90 91 while (multipart) { 92 multipart = false; 93 len = recv(sock, buf, sizeof(buf), 0); 94 if (len < 0) { 95 ret = -errno; 96 goto done; 97 } 98 99 if (len == 0) 100 break; 101 102 for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); 103 nh = NLMSG_NEXT(nh, len)) { 104 if (nh->nlmsg_pid != nl_pid) { 105 ret = -LIBBPF_ERRNO__WRNGPID; 106 goto done; 107 } 108 if (nh->nlmsg_seq != seq) { 109 ret = -LIBBPF_ERRNO__INVSEQ; 110 goto done; 111 } 112 if (nh->nlmsg_flags & NLM_F_MULTI) 113 multipart = true; 114 switch (nh->nlmsg_type) { 115 case NLMSG_ERROR: 116 err = (struct nlmsgerr *)NLMSG_DATA(nh); 117 if (!err->error) 118 continue; 119 ret = err->error; 120 libbpf_nla_dump_errormsg(nh); 121 goto done; 122 case NLMSG_DONE: 123 return 0; 124 default: 125 break; 126 } 127 if (_fn) { 128 ret = _fn(nh, fn, cookie); 129 if (ret) 130 return ret; 131 } 132 } 133 } 134 ret = 0; 135 done: 136 return ret; 137 } 138 139 static int libbpf_netlink_send_recv(struct nlmsghdr *nh, 140 __dump_nlmsg_t parse_msg, 141 libbpf_dump_nlmsg_t parse_attr, 142 void *cookie) 143 { 144 __u32 nl_pid = 0; 145 int sock, ret; 146 147 sock = libbpf_netlink_open(&nl_pid); 148 if (sock < 0) 149 return sock; 150 151 nh->nlmsg_pid = 0; 152 nh->nlmsg_seq = time(NULL); 153 154 if (send(sock, nh, nh->nlmsg_len, 0) < 0) { 155 ret = -errno; 156 goto out; 157 } 158 159 ret = libbpf_netlink_recv(sock, nl_pid, nh->nlmsg_seq, 160 parse_msg, parse_attr, cookie); 161 out: 162 libbpf_netlink_close(sock); 163 return ret; 164 } 165 166 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd, 167 __u32 flags) 168 { 169 struct nlattr *nla; 170 int ret; 171 struct { 172 struct nlmsghdr nh; 173 struct ifinfomsg ifinfo; 174 char attrbuf[64]; 175 } req; 176 177 memset(&req, 0, sizeof(req)); 178 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 179 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 180 req.nh.nlmsg_type = RTM_SETLINK; 181 req.ifinfo.ifi_family = AF_UNSPEC; 182 req.ifinfo.ifi_index = ifindex; 183 184 nla = nlattr_begin_nested(&req.nh, sizeof(req), IFLA_XDP); 185 if (!nla) 186 return -EMSGSIZE; 187 ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FD, &fd, sizeof(fd)); 188 if (ret < 0) 189 return ret; 190 if (flags) { 191 ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FLAGS, &flags, 192 sizeof(flags)); 193 if (ret < 0) 194 return ret; 195 } 196 if (flags & XDP_FLAGS_REPLACE) { 197 ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_EXPECTED_FD, 198 &old_fd, sizeof(old_fd)); 199 if (ret < 0) 200 return ret; 201 } 202 nlattr_end_nested(&req.nh, nla); 203 204 return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL); 205 } 206 207 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, 208 const struct bpf_xdp_set_link_opts *opts) 209 { 210 int old_fd = -1; 211 212 if (!OPTS_VALID(opts, bpf_xdp_set_link_opts)) 213 return -EINVAL; 214 215 if (OPTS_HAS(opts, old_fd)) { 216 old_fd = OPTS_GET(opts, old_fd, -1); 217 flags |= XDP_FLAGS_REPLACE; 218 } 219 220 return __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags); 221 } 222 223 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 224 { 225 return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags); 226 } 227 228 static int __dump_link_nlmsg(struct nlmsghdr *nlh, 229 libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 230 { 231 struct nlattr *tb[IFLA_MAX + 1], *attr; 232 struct ifinfomsg *ifi = NLMSG_DATA(nlh); 233 int len; 234 235 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 236 attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 237 238 if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 239 return -LIBBPF_ERRNO__NLPARSE; 240 241 return dump_link_nlmsg(cookie, ifi, tb); 242 } 243 244 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) 245 { 246 struct nlattr *xdp_tb[IFLA_XDP_MAX + 1]; 247 struct xdp_id_md *xdp_id = cookie; 248 struct ifinfomsg *ifinfo = msg; 249 int ret; 250 251 if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index) 252 return 0; 253 254 if (!tb[IFLA_XDP]) 255 return 0; 256 257 ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL); 258 if (ret) 259 return ret; 260 261 if (!xdp_tb[IFLA_XDP_ATTACHED]) 262 return 0; 263 264 xdp_id->info.attach_mode = libbpf_nla_getattr_u8( 265 xdp_tb[IFLA_XDP_ATTACHED]); 266 267 if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE) 268 return 0; 269 270 if (xdp_tb[IFLA_XDP_PROG_ID]) 271 xdp_id->info.prog_id = libbpf_nla_getattr_u32( 272 xdp_tb[IFLA_XDP_PROG_ID]); 273 274 if (xdp_tb[IFLA_XDP_SKB_PROG_ID]) 275 xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32( 276 xdp_tb[IFLA_XDP_SKB_PROG_ID]); 277 278 if (xdp_tb[IFLA_XDP_DRV_PROG_ID]) 279 xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32( 280 xdp_tb[IFLA_XDP_DRV_PROG_ID]); 281 282 if (xdp_tb[IFLA_XDP_HW_PROG_ID]) 283 xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32( 284 xdp_tb[IFLA_XDP_HW_PROG_ID]); 285 286 return 0; 287 } 288 289 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, 290 size_t info_size, __u32 flags) 291 { 292 struct xdp_id_md xdp_id = {}; 293 __u32 mask; 294 int ret; 295 struct { 296 struct nlmsghdr nh; 297 struct ifinfomsg ifm; 298 } req = { 299 .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 300 .nh.nlmsg_type = RTM_GETLINK, 301 .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 302 .ifm.ifi_family = AF_PACKET, 303 }; 304 305 if (flags & ~XDP_FLAGS_MASK || !info_size) 306 return -EINVAL; 307 308 /* Check whether the single {HW,DRV,SKB} mode is set */ 309 flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE); 310 mask = flags - 1; 311 if (flags && flags & mask) 312 return -EINVAL; 313 314 xdp_id.ifindex = ifindex; 315 xdp_id.flags = flags; 316 317 ret = libbpf_netlink_send_recv(&req.nh, __dump_link_nlmsg, 318 get_xdp_info, &xdp_id); 319 if (!ret) { 320 size_t sz = min(info_size, sizeof(xdp_id.info)); 321 322 memcpy(info, &xdp_id.info, sz); 323 memset((void *) info + sz, 0, info_size - sz); 324 } 325 326 return ret; 327 } 328 329 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags) 330 { 331 flags &= XDP_FLAGS_MODES; 332 333 if (info->attach_mode != XDP_ATTACHED_MULTI && !flags) 334 return info->prog_id; 335 if (flags & XDP_FLAGS_DRV_MODE) 336 return info->drv_prog_id; 337 if (flags & XDP_FLAGS_HW_MODE) 338 return info->hw_prog_id; 339 if (flags & XDP_FLAGS_SKB_MODE) 340 return info->skb_prog_id; 341 342 return 0; 343 } 344 345 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) 346 { 347 struct xdp_link_info info; 348 int ret; 349 350 ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags); 351 if (!ret) 352 *prog_id = get_xdp_id(&info, flags); 353 354 return ret; 355 } 356