diff options
Diffstat (limited to 'tools/lib/bpf/netlink.c')
-rw-r--r-- | tools/lib/bpf/netlink.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c new file mode 100644 index 000000000000..ccaa991fe9d8 --- /dev/null +++ b/tools/lib/bpf/netlink.c | |||
@@ -0,0 +1,165 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
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 "nlattr.h" | ||
16 | |||
17 | #ifndef SOL_NETLINK | ||
18 | #define SOL_NETLINK 270 | ||
19 | #endif | ||
20 | |||
21 | static int bpf_netlink_open(__u32 *nl_pid) | ||
22 | { | ||
23 | struct sockaddr_nl sa; | ||
24 | socklen_t addrlen; | ||
25 | int one = 1, ret; | ||
26 | int sock; | ||
27 | |||
28 | memset(&sa, 0, sizeof(sa)); | ||
29 | sa.nl_family = AF_NETLINK; | ||
30 | |||
31 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | ||
32 | if (sock < 0) | ||
33 | return -errno; | ||
34 | |||
35 | if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, | ||
36 | &one, sizeof(one)) < 0) { | ||
37 | fprintf(stderr, "Netlink error reporting not supported\n"); | ||
38 | } | ||
39 | |||
40 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { | ||
41 | ret = -errno; | ||
42 | goto cleanup; | ||
43 | } | ||
44 | |||
45 | addrlen = sizeof(sa); | ||
46 | if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { | ||
47 | ret = -errno; | ||
48 | goto cleanup; | ||
49 | } | ||
50 | |||
51 | if (addrlen != sizeof(sa)) { | ||
52 | ret = -LIBBPF_ERRNO__INTERNAL; | ||
53 | goto cleanup; | ||
54 | } | ||
55 | |||
56 | *nl_pid = sa.nl_pid; | ||
57 | return sock; | ||
58 | |||
59 | cleanup: | ||
60 | close(sock); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq) | ||
65 | { | ||
66 | struct nlmsgerr *err; | ||
67 | struct nlmsghdr *nh; | ||
68 | char buf[4096]; | ||
69 | int len, ret; | ||
70 | |||
71 | while (1) { | ||
72 | len = recv(sock, buf, sizeof(buf), 0); | ||
73 | if (len < 0) { | ||
74 | ret = -errno; | ||
75 | goto done; | ||
76 | } | ||
77 | |||
78 | for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); | ||
79 | nh = NLMSG_NEXT(nh, len)) { | ||
80 | if (nh->nlmsg_pid != nl_pid) { | ||
81 | ret = -LIBBPF_ERRNO__WRNGPID; | ||
82 | goto done; | ||
83 | } | ||
84 | if (nh->nlmsg_seq != seq) { | ||
85 | ret = -LIBBPF_ERRNO__INVSEQ; | ||
86 | goto done; | ||
87 | } | ||
88 | switch (nh->nlmsg_type) { | ||
89 | case NLMSG_ERROR: | ||
90 | err = (struct nlmsgerr *)NLMSG_DATA(nh); | ||
91 | if (!err->error) | ||
92 | continue; | ||
93 | ret = err->error; | ||
94 | nla_dump_errormsg(nh); | ||
95 | goto done; | ||
96 | case NLMSG_DONE: | ||
97 | return 0; | ||
98 | default: | ||
99 | break; | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | ret = 0; | ||
104 | done: | ||
105 | return ret; | ||
106 | } | ||
107 | |||
108 | int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) | ||
109 | { | ||
110 | int sock, seq = 0, ret; | ||
111 | struct nlattr *nla, *nla_xdp; | ||
112 | struct { | ||
113 | struct nlmsghdr nh; | ||
114 | struct ifinfomsg ifinfo; | ||
115 | char attrbuf[64]; | ||
116 | } req; | ||
117 | __u32 nl_pid; | ||
118 | |||
119 | sock = bpf_netlink_open(&nl_pid); | ||
120 | if (sock < 0) | ||
121 | return sock; | ||
122 | |||
123 | memset(&req, 0, sizeof(req)); | ||
124 | req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | ||
125 | req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | ||
126 | req.nh.nlmsg_type = RTM_SETLINK; | ||
127 | req.nh.nlmsg_pid = 0; | ||
128 | req.nh.nlmsg_seq = ++seq; | ||
129 | req.ifinfo.ifi_family = AF_UNSPEC; | ||
130 | req.ifinfo.ifi_index = ifindex; | ||
131 | |||
132 | /* started nested attribute for XDP */ | ||
133 | nla = (struct nlattr *)(((char *)&req) | ||
134 | + NLMSG_ALIGN(req.nh.nlmsg_len)); | ||
135 | nla->nla_type = NLA_F_NESTED | IFLA_XDP; | ||
136 | nla->nla_len = NLA_HDRLEN; | ||
137 | |||
138 | /* add XDP fd */ | ||
139 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); | ||
140 | nla_xdp->nla_type = IFLA_XDP_FD; | ||
141 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); | ||
142 | memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); | ||
143 | nla->nla_len += nla_xdp->nla_len; | ||
144 | |||
145 | /* if user passed in any flags, add those too */ | ||
146 | if (flags) { | ||
147 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); | ||
148 | nla_xdp->nla_type = IFLA_XDP_FLAGS; | ||
149 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); | ||
150 | memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); | ||
151 | nla->nla_len += nla_xdp->nla_len; | ||
152 | } | ||
153 | |||
154 | req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); | ||
155 | |||
156 | if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { | ||
157 | ret = -errno; | ||
158 | goto cleanup; | ||
159 | } | ||
160 | ret = bpf_netlink_recv(sock, nl_pid, seq); | ||
161 | |||
162 | cleanup: | ||
163 | close(sock); | ||
164 | return ret; | ||
165 | } | ||