Commit 8bbb77b7 authored by Kumar Kartikeya Dwivedi's avatar Kumar Kartikeya Dwivedi Committed by Daniel Borkmann

libbpf: Add various netlink helpers

This change introduces a few helpers to wrap open coded attribute
preparation in netlink.c. It also adds a libbpf_netlink_send_recv() that
is useful to wrap send + recv handling in a generic way. Subsequent patch
will also use this function for sending and receiving a netlink response.
The libbpf_nl_get_link() helper has been removed instead, moving socket
creation into the newly named libbpf_netlink_send_recv().

Every nested attribute's closure must happen using the helper
nlattr_end_nested(), which sets its length properly. NLA_F_NESTED is
enforced using nlattr_begin_nested() helper. Other simple attributes
can be added directly.

The maxsz parameter corresponds to the size of the request structure
which is being filled in, so for instance with req being:

  struct {
	struct nlmsghdr nh;
	struct tcmsg t;
	char buf[4096];
  } req;

Then, maxsz should be sizeof(req).

This change also converts the open coded attribute preparation with these
helpers. Note that the only failure the internal call to nlattr_add()
could result in the nested helper would be -EMSGSIZE, hence that is what
we return to our caller.

The libbpf_netlink_send_recv() call takes care of opening the socket,
sending the netlink message, receiving the response, potentially invoking
callbacks, and return errors if any, and then finally close the socket.
This allows users to avoid identical socket setup code in different places.
The only user of libbpf_nl_get_link() has been converted to make use of it.
__bpf_set_link_xdp_fd_replace() has also been refactored to use it.
Signed-off-by: default avatarKumar Kartikeya Dwivedi <memxor@gmail.com>
[ Daniel: major patch cleanup ]
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Reviewed-by: default avatarToke Høiland-Jørgensen <toke@redhat.com>
Link: https://lore.kernel.org/bpf/20210512103451.989420-2-memxor@gmail.com
parent 513f485c
...@@ -73,9 +73,14 @@ static int libbpf_netlink_open(__u32 *nl_pid) ...@@ -73,9 +73,14 @@ static int libbpf_netlink_open(__u32 *nl_pid)
return ret; return ret;
} }
static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, static void libbpf_netlink_close(int sock)
__dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, {
void *cookie) close(sock);
}
static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
__dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
void *cookie)
{ {
bool multipart = true; bool multipart = true;
struct nlmsgerr *err; struct nlmsgerr *err;
...@@ -131,72 +136,72 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, ...@@ -131,72 +136,72 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
return ret; return ret;
} }
static int libbpf_netlink_send_recv(struct nlmsghdr *nh,
__dump_nlmsg_t parse_msg,
libbpf_dump_nlmsg_t parse_attr,
void *cookie)
{
__u32 nl_pid = 0;
int sock, ret;
sock = libbpf_netlink_open(&nl_pid);
if (sock < 0)
return sock;
nh->nlmsg_pid = 0;
nh->nlmsg_seq = time(NULL);
if (send(sock, nh, nh->nlmsg_len, 0) < 0) {
ret = -errno;
goto out;
}
ret = libbpf_netlink_recv(sock, nl_pid, nh->nlmsg_seq,
parse_msg, parse_attr, cookie);
out:
libbpf_netlink_close(sock);
return ret;
}
static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd, static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
__u32 flags) __u32 flags)
{ {
int sock, seq = 0, ret; struct nlattr *nla;
struct nlattr *nla, *nla_xdp; int ret;
struct { struct {
struct nlmsghdr nh; struct nlmsghdr nh;
struct ifinfomsg ifinfo; struct ifinfomsg ifinfo;
char attrbuf[64]; char attrbuf[64];
} req; } req;
__u32 nl_pid = 0;
sock = libbpf_netlink_open(&nl_pid);
if (sock < 0)
return sock;
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.nh.nlmsg_type = RTM_SETLINK; req.nh.nlmsg_type = RTM_SETLINK;
req.nh.nlmsg_pid = 0;
req.nh.nlmsg_seq = ++seq;
req.ifinfo.ifi_family = AF_UNSPEC; req.ifinfo.ifi_family = AF_UNSPEC;
req.ifinfo.ifi_index = ifindex; req.ifinfo.ifi_index = ifindex;
/* started nested attribute for XDP */ nla = nlattr_begin_nested(&req.nh, sizeof(req), IFLA_XDP);
nla = (struct nlattr *)(((char *)&req) if (!nla)
+ NLMSG_ALIGN(req.nh.nlmsg_len)); return -EMSGSIZE;
nla->nla_type = NLA_F_NESTED | IFLA_XDP; ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FD, &fd, sizeof(fd));
nla->nla_len = NLA_HDRLEN; if (ret < 0)
return ret;
/* add XDP fd */
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
nla_xdp->nla_type = IFLA_XDP_FD;
nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
nla->nla_len += nla_xdp->nla_len;
/* if user passed in any flags, add those too */
if (flags) { if (flags) {
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FLAGS, &flags,
nla_xdp->nla_type = IFLA_XDP_FLAGS; sizeof(flags));
nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); if (ret < 0)
memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); return ret;
nla->nla_len += nla_xdp->nla_len;
} }
if (flags & XDP_FLAGS_REPLACE) { if (flags & XDP_FLAGS_REPLACE) {
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_EXPECTED_FD,
nla_xdp->nla_type = IFLA_XDP_EXPECTED_FD; &old_fd, sizeof(old_fd));
nla_xdp->nla_len = NLA_HDRLEN + sizeof(old_fd); if (ret < 0)
memcpy((char *)nla_xdp + NLA_HDRLEN, &old_fd, sizeof(old_fd)); return ret;
nla->nla_len += nla_xdp->nla_len;
} }
nlattr_end_nested(&req.nh, nla);
req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
ret = -errno;
goto cleanup;
}
ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
cleanup:
close(sock);
return ret;
} }
int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
...@@ -212,9 +217,7 @@ int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, ...@@ -212,9 +217,7 @@ int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
flags |= XDP_FLAGS_REPLACE; flags |= XDP_FLAGS_REPLACE;
} }
return __bpf_set_link_xdp_fd_replace(ifindex, fd, return __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags);
old_fd,
flags);
} }
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
...@@ -231,6 +234,7 @@ static int __dump_link_nlmsg(struct nlmsghdr *nlh, ...@@ -231,6 +234,7 @@ static int __dump_link_nlmsg(struct nlmsghdr *nlh,
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
return -LIBBPF_ERRNO__NLPARSE; return -LIBBPF_ERRNO__NLPARSE;
...@@ -282,16 +286,21 @@ static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) ...@@ -282,16 +286,21 @@ static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
return 0; return 0;
} }
static int libbpf_nl_get_link(int sock, unsigned int nl_pid,
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
size_t info_size, __u32 flags) size_t info_size, __u32 flags)
{ {
struct xdp_id_md xdp_id = {}; struct xdp_id_md xdp_id = {};
int sock, ret;
__u32 nl_pid = 0;
__u32 mask; __u32 mask;
int ret;
struct {
struct nlmsghdr nh;
struct ifinfomsg ifm;
} req = {
.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nh.nlmsg_type = RTM_GETLINK,
.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
.ifm.ifi_family = AF_PACKET,
};
if (flags & ~XDP_FLAGS_MASK || !info_size) if (flags & ~XDP_FLAGS_MASK || !info_size)
return -EINVAL; return -EINVAL;
...@@ -302,14 +311,11 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, ...@@ -302,14 +311,11 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
if (flags && flags & mask) if (flags && flags & mask)
return -EINVAL; return -EINVAL;
sock = libbpf_netlink_open(&nl_pid);
if (sock < 0)
return sock;
xdp_id.ifindex = ifindex; xdp_id.ifindex = ifindex;
xdp_id.flags = flags; xdp_id.flags = flags;
ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id); ret = libbpf_netlink_send_recv(&req.nh, __dump_link_nlmsg,
get_xdp_info, &xdp_id);
if (!ret) { if (!ret) {
size_t sz = min(info_size, sizeof(xdp_id.info)); size_t sz = min(info_size, sizeof(xdp_id.info));
...@@ -317,7 +323,6 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, ...@@ -317,7 +323,6 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
memset((void *) info + sz, 0, info_size - sz); memset((void *) info + sz, 0, info_size - sz);
} }
close(sock);
return ret; return ret;
} }
...@@ -348,25 +353,3 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) ...@@ -348,25 +353,3 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
return ret; return ret;
} }
int libbpf_nl_get_link(int sock, unsigned int nl_pid,
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
{
struct {
struct nlmsghdr nlh;
struct ifinfomsg ifm;
} req = {
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nlh.nlmsg_type = RTM_GETLINK,
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
.ifm.ifi_family = AF_PACKET,
};
int seq = time(NULL);
req.nlh.nlmsg_seq = seq;
if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
return -errno;
return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
dump_link_nlmsg, cookie);
}
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
#define __LIBBPF_NLATTR_H #define __LIBBPF_NLATTR_H
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <errno.h>
#include <linux/netlink.h> #include <linux/netlink.h>
/* avoid multiple definition of netlink features */ /* avoid multiple definition of netlink features */
#define __LINUX_NETLINK_H #define __LINUX_NETLINK_H
...@@ -103,4 +106,49 @@ int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype, ...@@ -103,4 +106,49 @@ int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype,
int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh); int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh);
static inline struct nlattr *nla_data(struct nlattr *nla)
{
return (struct nlattr *)((char *)nla + NLA_HDRLEN);
}
static inline struct nlattr *nh_tail(struct nlmsghdr *nh)
{
return (struct nlattr *)((char *)nh + NLMSG_ALIGN(nh->nlmsg_len));
}
static inline int nlattr_add(struct nlmsghdr *nh, size_t maxsz, int type,
const void *data, int len)
{
struct nlattr *nla;
if (NLMSG_ALIGN(nh->nlmsg_len) + NLA_ALIGN(NLA_HDRLEN + len) > maxsz)
return -EMSGSIZE;
if (!!data != !!len)
return -EINVAL;
nla = nh_tail(nh);
nla->nla_type = type;
nla->nla_len = NLA_HDRLEN + len;
if (data)
memcpy(nla_data(nla), data, len);
nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + NLA_ALIGN(nla->nla_len);
return 0;
}
static inline struct nlattr *nlattr_begin_nested(struct nlmsghdr *nh,
size_t maxsz, int type)
{
struct nlattr *tail;
tail = nh_tail(nh);
if (nlattr_add(nh, maxsz, type | NLA_F_NESTED, NULL, 0))
return NULL;
return tail;
}
static inline void nlattr_end_nested(struct nlmsghdr *nh, struct nlattr *tail)
{
tail->nla_len = (char *)nh_tail(nh) - (char *)tail;
}
#endif /* __LIBBPF_NLATTR_H */ #endif /* __LIBBPF_NLATTR_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment