1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
5 * Based on wireguard-tools:
6 * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
17 #include <sys/types.h>
18 #include <sys/socket.h>
21 #include <arpa/inet.h>
22 #include <linux/rtnetlink.h>
23 #include <netlink/msg.h>
24 #include <netlink/attr.h>
25 #include <netlink/socket.h>
28 #include "linux/wireguard.h"
36 struct wg_linux_peer_req
{
39 struct nlattr
*peers
, *entry
;
42 static struct unl unl
;
50 return unl_genl_init(&unl
, "wireguard");
53 static struct nl_msg
*
54 wg_genl_msg(struct network
*net
, bool set
)
58 msg
= unl_genl_msg(&unl
, set
? WG_CMD_SET_DEVICE
: WG_CMD_GET_DEVICE
, !set
);
59 nla_put_string(msg
, WGDEVICE_A_IFNAME
, network_name(net
));
65 wg_genl_call(struct nl_msg
*msg
)
67 return unl_request(&unl
, msg
, NULL
, NULL
);
71 __wg_linux_init(struct network
*net
, void *key
)
75 msg
= wg_genl_msg(net
, true);
76 nla_put(msg
, WGDEVICE_A_PRIVATE_KEY
, WG_KEY_LEN
, key
);
77 nla_put_u32(msg
, WGDEVICE_A_FLAGS
, WGDEVICE_F_REPLACE_PEERS
);
79 return wg_genl_call(msg
);
83 wg_linux_cleanup(struct network
*net
)
85 uint8_t key
[WG_KEY_LEN
] = {};
87 __wg_linux_init(net
, key
);
91 wg_linux_init(struct network
*net
)
96 return __wg_linux_init(net
, net
->config
.key
);
100 wg_linux_init_local(struct network
*net
, struct network_peer
*peer
)
104 msg
= wg_genl_msg(net
, true);
105 nla_put_u16(msg
, WGDEVICE_A_LISTEN_PORT
, peer
->port
);
107 return wg_genl_call(msg
);
111 wg_linux_msg_add_ip(struct nl_msg
*msg
, int af
, void *addr
, int mask
)
117 len
= sizeof(struct in6_addr
);
119 len
= sizeof(struct in_addr
);
121 ip
= nla_nest_start(msg
, 0);
122 nla_put_u16(msg
, WGALLOWEDIP_A_FAMILY
, af
);
123 nla_put(msg
, WGALLOWEDIP_A_IPADDR
, len
, addr
);
124 nla_put_u8(msg
, WGALLOWEDIP_A_CIDR_MASK
, mask
);
125 nla_nest_end(msg
, ip
);
128 static struct nl_msg
*
129 wg_linux_peer_req_init(struct network
*net
, struct network_peer
*peer
,
130 struct wg_linux_peer_req
*req
)
132 req
->msg
= wg_genl_msg(net
, true);
134 req
->peers
= nla_nest_start(req
->msg
, WGDEVICE_A_PEERS
);
135 req
->entry
= nla_nest_start(req
->msg
, 0);
136 nla_put(req
->msg
, WGPEER_A_PUBLIC_KEY
, WG_KEY_LEN
, peer
->key
);
142 wg_linux_peer_req_done(struct wg_linux_peer_req
*req
)
144 nla_nest_end(req
->msg
, req
->entry
);
145 nla_nest_end(req
->msg
, req
->peers
);
147 return wg_genl_call(req
->msg
);
151 wg_linux_peer_msg_add_allowed_ip(struct nl_msg
*msg
, struct network_peer
*peer
)
153 struct blob_attr
*cur
;
156 wg_linux_msg_add_ip(msg
, AF_INET6
, &peer
->local_addr
.in6
, 128);
158 blobmsg_for_each_attr(cur
, peer
->ipaddr
, rem
) {
159 const char *str
= blobmsg_get_string(cur
);
163 if (strchr(str
, ':')) {
171 if (inet_pton(af
, str
, &in6
) != 1)
174 wg_linux_msg_add_ip(msg
, af
, &in6
, mask
);
177 blobmsg_for_each_attr(cur
, peer
->subnet
, rem
) {
178 const char *str
= blobmsg_get_string(cur
);
179 union network_addr addr
;
183 af
= strchr(str
, ':') ? AF_INET6
: AF_INET
;
184 if (network_get_subnet(af
, &addr
, &mask
, str
))
187 wg_linux_msg_add_ip(msg
, af
, &addr
, mask
);
193 wg_linux_peer_update(struct network
*net
, struct network_peer
*peer
, enum wg_update_cmd cmd
)
195 struct wg_linux_peer_req req
;
196 struct network_host
*host
;
200 msg
= wg_linux_peer_req_init(net
, peer
, &req
);
202 if (cmd
== WG_PEER_DELETE
) {
203 nla_put_u32(msg
, WGPEER_A_FLAGS
, WGPEER_F_REMOVE_ME
);
207 nla_put_u32(msg
, WGPEER_A_FLAGS
, WGPEER_F_REPLACE_ALLOWEDIPS
);
209 ips
= nla_nest_start(msg
, WGPEER_A_ALLOWEDIPS
);
211 wg_linux_peer_msg_add_allowed_ip(msg
, peer
);
212 for_each_routed_host(host
, net
, peer
)
213 wg_linux_peer_msg_add_allowed_ip(msg
, &host
->peer
);
215 nla_nest_end(msg
, ips
);
218 return wg_linux_peer_req_done(&req
);
222 wg_linux_parse_peer(struct network
*net
, struct nlattr
*data
, time_t now
)
224 struct network_peer
*peer
= NULL
;
225 struct nlattr
*tb
[__WGPEER_A_LAST
];
228 nla_parse_nested(tb
, WGPEER_A_MAX
, data
, NULL
);
230 cur
= tb
[WGPEER_A_PUBLIC_KEY
];
234 peer
= wg_peer_update_start(net
, nla_data(cur
));
238 if ((cur
= tb
[WGPEER_A_LAST_HANDSHAKE_TIME
]) != NULL
) {
239 struct timespec64
*tv
= nla_data(cur
);
241 wg_peer_set_last_handshake(net
, peer
, now
, tv
->tv_sec
);
244 if ((cur
= tb
[WGPEER_A_RX_BYTES
]) != NULL
)
245 wg_peer_set_rx_bytes(net
, peer
, nla_get_u64(cur
));
247 if ((cur
= tb
[WGPEER_A_ENDPOINT
]) != NULL
)
248 wg_peer_set_endpoint(net
, peer
, nla_data(cur
), nla_len(cur
));
250 wg_peer_update_done(net
, peer
);
254 wg_linux_parse_peer_list(struct network
*net
, struct nlattr
*data
, time_t now
)
262 nla_for_each_nested(cur
, data
, rem
)
263 wg_linux_parse_peer(net
, cur
, now
);
267 wg_linux_get_cb(struct nl_msg
*msg
, void *arg
)
269 struct nlmsghdr
*nh
= nlmsg_hdr(msg
);
270 struct network
*net
= arg
;
271 struct nlattr
*tb
[__WGDEVICE_A_LAST
];
272 time_t now
= time(NULL
);
274 nlmsg_parse(nh
, sizeof(struct genlmsghdr
), tb
, __WGDEVICE_A_LAST
, NULL
);
275 wg_linux_parse_peer_list(net
, tb
[WGDEVICE_A_PEERS
], now
);
281 wg_linux_peer_refresh(struct network
*net
)
283 struct nl_msg
*msg
= wg_genl_msg(net
, false);
285 return unl_request(&unl
, msg
, wg_linux_get_cb
, net
);
289 wg_linux_peer_connect(struct network
*net
, struct network_peer
*peer
,
290 union network_endpoint
*ep
)
292 struct wg_linux_peer_req req
;
296 msg
= wg_linux_peer_req_init(net
, peer
, &req
);
298 if (net
->net_config
.keepalive
) {
299 nla_put_u16(msg
, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL
, 0);
300 wg_linux_peer_req_done(&req
);
302 msg
= wg_linux_peer_req_init(net
, peer
, &req
);
303 nla_put_u16(msg
, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL
,
304 net
->net_config
.keepalive
);
307 if (ep
->in
.sin_family
== AF_INET6
)
308 len
= sizeof(ep
->in6
);
310 len
= sizeof(ep
->in
);
311 nla_put(msg
, WGPEER_A_ENDPOINT
, len
, &ep
->in6
);
313 return wg_linux_peer_req_done(&req
);
316 const struct wg_ops wg_linux_ops
= {
318 .init
= wg_linux_init
,
319 .cleanup
= wg_linux_cleanup
,
320 .init_local
= wg_linux_init_local
,
321 .peer_update
= wg_linux_peer_update
,
322 .peer_refresh
= wg_linux_peer_refresh
,
323 .peer_connect
= wg_linux_peer_connect
,