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