1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 /* Copyright (c) 2018 Facebook */
8 #include <linux/rtnetlink.h>
9 #include <sys/socket.h>
15 #include "libbpf_internal.h"
19 #define SOL_NETLINK 270
22 typedef int (*libbpf_dump_nlmsg_t
)(void *cookie
, void *msg
, struct nlattr
**tb
);
24 typedef int (*__dump_nlmsg_t
)(struct nlmsghdr
*nlmsg
, libbpf_dump_nlmsg_t
,
30 struct xdp_link_info info
;
33 static int libbpf_netlink_open(__u32
*nl_pid
)
35 struct sockaddr_nl sa
;
40 memset(&sa
, 0, sizeof(sa
));
41 sa
.nl_family
= AF_NETLINK
;
43 sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
47 if (setsockopt(sock
, SOL_NETLINK
, NETLINK_EXT_ACK
,
48 &one
, sizeof(one
)) < 0) {
49 pr_warn("Netlink error reporting not supported\n");
52 if (bind(sock
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0) {
58 if (getsockname(sock
, (struct sockaddr
*)&sa
, &addrlen
) < 0) {
63 if (addrlen
!= sizeof(sa
)) {
64 ret
= -LIBBPF_ERRNO__INTERNAL
;
76 static int bpf_netlink_recv(int sock
, __u32 nl_pid
, int seq
,
77 __dump_nlmsg_t _fn
, libbpf_dump_nlmsg_t fn
,
80 bool multipart
= true;
88 len
= recv(sock
, buf
, sizeof(buf
), 0);
97 for (nh
= (struct nlmsghdr
*)buf
; NLMSG_OK(nh
, len
);
98 nh
= NLMSG_NEXT(nh
, len
)) {
99 if (nh
->nlmsg_pid
!= nl_pid
) {
100 ret
= -LIBBPF_ERRNO__WRNGPID
;
103 if (nh
->nlmsg_seq
!= seq
) {
104 ret
= -LIBBPF_ERRNO__INVSEQ
;
107 if (nh
->nlmsg_flags
& NLM_F_MULTI
)
109 switch (nh
->nlmsg_type
) {
111 err
= (struct nlmsgerr
*)NLMSG_DATA(nh
);
115 libbpf_nla_dump_errormsg(nh
);
123 ret
= _fn(nh
, fn
, cookie
);
134 static int __bpf_set_link_xdp_fd_replace(int ifindex
, int fd
, int old_fd
,
137 int sock
, seq
= 0, ret
;
138 struct nlattr
*nla
, *nla_xdp
;
141 struct ifinfomsg ifinfo
;
146 sock
= libbpf_netlink_open(&nl_pid
);
150 memset(&req
, 0, sizeof(req
));
151 req
.nh
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
152 req
.nh
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_ACK
;
153 req
.nh
.nlmsg_type
= RTM_SETLINK
;
154 req
.nh
.nlmsg_pid
= 0;
155 req
.nh
.nlmsg_seq
= ++seq
;
156 req
.ifinfo
.ifi_family
= AF_UNSPEC
;
157 req
.ifinfo
.ifi_index
= ifindex
;
159 /* started nested attribute for XDP */
160 nla
= (struct nlattr
*)(((char *)&req
)
161 + NLMSG_ALIGN(req
.nh
.nlmsg_len
));
162 nla
->nla_type
= NLA_F_NESTED
| IFLA_XDP
;
163 nla
->nla_len
= NLA_HDRLEN
;
166 nla_xdp
= (struct nlattr
*)((char *)nla
+ nla
->nla_len
);
167 nla_xdp
->nla_type
= IFLA_XDP_FD
;
168 nla_xdp
->nla_len
= NLA_HDRLEN
+ sizeof(int);
169 memcpy((char *)nla_xdp
+ NLA_HDRLEN
, &fd
, sizeof(fd
));
170 nla
->nla_len
+= nla_xdp
->nla_len
;
172 /* if user passed in any flags, add those too */
174 nla_xdp
= (struct nlattr
*)((char *)nla
+ nla
->nla_len
);
175 nla_xdp
->nla_type
= IFLA_XDP_FLAGS
;
176 nla_xdp
->nla_len
= NLA_HDRLEN
+ sizeof(flags
);
177 memcpy((char *)nla_xdp
+ NLA_HDRLEN
, &flags
, sizeof(flags
));
178 nla
->nla_len
+= nla_xdp
->nla_len
;
181 if (flags
& XDP_FLAGS_REPLACE
) {
182 nla_xdp
= (struct nlattr
*)((char *)nla
+ nla
->nla_len
);
183 nla_xdp
->nla_type
= IFLA_XDP_EXPECTED_FD
;
184 nla_xdp
->nla_len
= NLA_HDRLEN
+ sizeof(old_fd
);
185 memcpy((char *)nla_xdp
+ NLA_HDRLEN
, &old_fd
, sizeof(old_fd
));
186 nla
->nla_len
+= nla_xdp
->nla_len
;
189 req
.nh
.nlmsg_len
+= NLA_ALIGN(nla
->nla_len
);
191 if (send(sock
, &req
, req
.nh
.nlmsg_len
, 0) < 0) {
195 ret
= bpf_netlink_recv(sock
, nl_pid
, seq
, NULL
, NULL
, NULL
);
202 int bpf_set_link_xdp_fd_opts(int ifindex
, int fd
, __u32 flags
,
203 const struct bpf_xdp_set_link_opts
*opts
)
207 if (!OPTS_VALID(opts
, bpf_xdp_set_link_opts
))
210 if (OPTS_HAS(opts
, old_fd
)) {
211 old_fd
= OPTS_GET(opts
, old_fd
, -1);
212 flags
|= XDP_FLAGS_REPLACE
;
215 return __bpf_set_link_xdp_fd_replace(ifindex
, fd
,
220 int bpf_set_link_xdp_fd(int ifindex
, int fd
, __u32 flags
)
222 return __bpf_set_link_xdp_fd_replace(ifindex
, fd
, 0, flags
);
225 static int __dump_link_nlmsg(struct nlmsghdr
*nlh
,
226 libbpf_dump_nlmsg_t dump_link_nlmsg
, void *cookie
)
228 struct nlattr
*tb
[IFLA_MAX
+ 1], *attr
;
229 struct ifinfomsg
*ifi
= NLMSG_DATA(nlh
);
232 len
= nlh
->nlmsg_len
- NLMSG_LENGTH(sizeof(*ifi
));
233 attr
= (struct nlattr
*) ((void *) ifi
+ NLMSG_ALIGN(sizeof(*ifi
)));
234 if (libbpf_nla_parse(tb
, IFLA_MAX
, attr
, len
, NULL
) != 0)
235 return -LIBBPF_ERRNO__NLPARSE
;
237 return dump_link_nlmsg(cookie
, ifi
, tb
);
240 static int get_xdp_info(void *cookie
, void *msg
, struct nlattr
**tb
)
242 struct nlattr
*xdp_tb
[IFLA_XDP_MAX
+ 1];
243 struct xdp_id_md
*xdp_id
= cookie
;
244 struct ifinfomsg
*ifinfo
= msg
;
247 if (xdp_id
->ifindex
&& xdp_id
->ifindex
!= ifinfo
->ifi_index
)
253 ret
= libbpf_nla_parse_nested(xdp_tb
, IFLA_XDP_MAX
, tb
[IFLA_XDP
], NULL
);
257 if (!xdp_tb
[IFLA_XDP_ATTACHED
])
260 xdp_id
->info
.attach_mode
= libbpf_nla_getattr_u8(
261 xdp_tb
[IFLA_XDP_ATTACHED
]);
263 if (xdp_id
->info
.attach_mode
== XDP_ATTACHED_NONE
)
266 if (xdp_tb
[IFLA_XDP_PROG_ID
])
267 xdp_id
->info
.prog_id
= libbpf_nla_getattr_u32(
268 xdp_tb
[IFLA_XDP_PROG_ID
]);
270 if (xdp_tb
[IFLA_XDP_SKB_PROG_ID
])
271 xdp_id
->info
.skb_prog_id
= libbpf_nla_getattr_u32(
272 xdp_tb
[IFLA_XDP_SKB_PROG_ID
]);
274 if (xdp_tb
[IFLA_XDP_DRV_PROG_ID
])
275 xdp_id
->info
.drv_prog_id
= libbpf_nla_getattr_u32(
276 xdp_tb
[IFLA_XDP_DRV_PROG_ID
]);
278 if (xdp_tb
[IFLA_XDP_HW_PROG_ID
])
279 xdp_id
->info
.hw_prog_id
= libbpf_nla_getattr_u32(
280 xdp_tb
[IFLA_XDP_HW_PROG_ID
]);
285 static int libbpf_nl_get_link(int sock
, unsigned int nl_pid
,
286 libbpf_dump_nlmsg_t dump_link_nlmsg
, void *cookie
);
288 int bpf_get_link_xdp_info(int ifindex
, struct xdp_link_info
*info
,
289 size_t info_size
, __u32 flags
)
291 struct xdp_id_md xdp_id
= {};
296 if (flags
& ~XDP_FLAGS_MASK
|| !info_size
)
299 /* Check whether the single {HW,DRV,SKB} mode is set */
300 flags
&= (XDP_FLAGS_SKB_MODE
| XDP_FLAGS_DRV_MODE
| XDP_FLAGS_HW_MODE
);
302 if (flags
&& flags
& mask
)
305 sock
= libbpf_netlink_open(&nl_pid
);
309 xdp_id
.ifindex
= ifindex
;
310 xdp_id
.flags
= flags
;
312 ret
= libbpf_nl_get_link(sock
, nl_pid
, get_xdp_info
, &xdp_id
);
314 size_t sz
= min(info_size
, sizeof(xdp_id
.info
));
316 memcpy(info
, &xdp_id
.info
, sz
);
317 memset((void *) info
+ sz
, 0, info_size
- sz
);
324 static __u32
get_xdp_id(struct xdp_link_info
*info
, __u32 flags
)
326 flags
&= XDP_FLAGS_MODES
;
328 if (info
->attach_mode
!= XDP_ATTACHED_MULTI
&& !flags
)
329 return info
->prog_id
;
330 if (flags
& XDP_FLAGS_DRV_MODE
)
331 return info
->drv_prog_id
;
332 if (flags
& XDP_FLAGS_HW_MODE
)
333 return info
->hw_prog_id
;
334 if (flags
& XDP_FLAGS_SKB_MODE
)
335 return info
->skb_prog_id
;
340 int bpf_get_link_xdp_id(int ifindex
, __u32
*prog_id
, __u32 flags
)
342 struct xdp_link_info info
;
345 ret
= bpf_get_link_xdp_info(ifindex
, &info
, sizeof(info
), flags
);
347 *prog_id
= get_xdp_id(&info
, flags
);
352 int libbpf_nl_get_link(int sock
, unsigned int nl_pid
,
353 libbpf_dump_nlmsg_t dump_link_nlmsg
, void *cookie
)
357 struct ifinfomsg ifm
;
359 .nlh
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
)),
360 .nlh
.nlmsg_type
= RTM_GETLINK
,
361 .nlh
.nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
,
362 .ifm
.ifi_family
= AF_PACKET
,
364 int seq
= time(NULL
);
366 req
.nlh
.nlmsg_seq
= seq
;
367 if (send(sock
, &req
, req
.nlh
.nlmsg_len
, 0) < 0)
370 return bpf_netlink_recv(sock
, nl_pid
, seq
, __dump_link_nlmsg
,
371 dump_link_nlmsg
, cookie
);