1 // SPDX-License-Identifier: GPL-2.0+
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 <linux/wireguard.h>
24 #include <netlink/msg.h>
25 #include <netlink/attr.h>
26 #include <netlink/socket.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_update(struct network
*net
, struct network_peer
*peer
, enum wg_update_cmd cmd
)
153 struct wg_linux_peer_req req
;
154 struct blob_attr
*cur
;
159 msg
= wg_linux_peer_req_init(net
, peer
, &req
);
161 if (cmd
== WG_PEER_DELETE
) {
162 nla_put_u32(msg
, WGPEER_A_FLAGS
, WGPEER_F_REMOVE_ME
);
166 nla_put_u32(msg
, WGPEER_A_FLAGS
, WGPEER_F_REPLACE_ALLOWEDIPS
);
168 ips
= nla_nest_start(msg
, WGPEER_A_ALLOWEDIPS
);
169 wg_linux_msg_add_ip(msg
, AF_INET6
, &peer
->local_addr
.in6
, 128);
171 blobmsg_for_each_attr(cur
, peer
->ipaddr
, rem
) {
172 const char *str
= blobmsg_get_string(cur
);
176 if (strchr(str
, ':')) {
184 if (inet_pton(af
, str
, &in6
) != 1)
187 wg_linux_msg_add_ip(msg
, af
, &in6
, mask
);
190 blobmsg_for_each_attr(cur
, peer
->subnet
, rem
) {
191 const char *str
= blobmsg_get_string(cur
);
192 union network_addr addr
;
196 af
= strchr(str
, ':') ? AF_INET6
: AF_INET
;
197 if (network_get_subnet(af
, &addr
, &mask
, str
))
200 wg_linux_msg_add_ip(msg
, af
, &addr
, mask
);
203 nla_nest_end(msg
, ips
);
206 return wg_linux_peer_req_done(&req
);
210 wg_linux_parse_peer(struct network
*net
, struct nlattr
*data
, time_t now
)
212 struct network_peer
*peer
= NULL
;
213 struct nlattr
*tb
[__WGPEER_A_LAST
];
216 nla_parse_nested(tb
, WGPEER_A_MAX
, data
, NULL
);
218 cur
= tb
[WGPEER_A_PUBLIC_KEY
];
222 peer
= wg_peer_update_start(net
, nla_data(cur
));
226 if ((cur
= tb
[WGPEER_A_LAST_HANDSHAKE_TIME
]) != NULL
) {
227 struct timespec64
*tv
= nla_data(cur
);
229 wg_peer_set_last_handshake(net
, peer
, now
, tv
->tv_sec
);
232 if ((cur
= tb
[WGPEER_A_RX_BYTES
]) != NULL
)
233 wg_peer_set_rx_bytes(net
, peer
, nla_get_u64(cur
));
235 if ((cur
= tb
[WGPEER_A_ENDPOINT
]) != NULL
)
236 wg_peer_set_endpoint(net
, peer
, nla_data(cur
), nla_len(cur
));
238 wg_peer_update_done(net
, peer
);
242 wg_linux_parse_peer_list(struct network
*net
, struct nlattr
*data
, time_t now
)
250 nla_for_each_nested(cur
, data
, rem
)
251 wg_linux_parse_peer(net
, cur
, now
);
255 wg_linux_get_cb(struct nl_msg
*msg
, void *arg
)
257 struct nlmsghdr
*nh
= nlmsg_hdr(msg
);
258 struct network
*net
= arg
;
259 struct nlattr
*tb
[__WGDEVICE_A_LAST
];
260 time_t now
= time(NULL
);
262 nlmsg_parse(nh
, sizeof(struct genlmsghdr
), tb
, __WGDEVICE_A_LAST
, NULL
);
263 wg_linux_parse_peer_list(net
, tb
[WGDEVICE_A_PEERS
], now
);
269 wg_linux_peer_refresh(struct network
*net
)
271 struct nl_msg
*msg
= wg_genl_msg(net
, false);
273 return unl_request(&unl
, msg
, wg_linux_get_cb
, net
);
277 wg_linux_peer_connect(struct network
*net
, struct network_peer
*peer
,
278 union network_endpoint
*ep
)
280 struct wg_linux_peer_req req
;
284 msg
= wg_linux_peer_req_init(net
, peer
, &req
);
286 if (net
->net_config
.keepalive
) {
287 nla_put_u16(msg
, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL
, 0);
288 wg_linux_peer_req_done(&req
);
290 msg
= wg_linux_peer_req_init(net
, peer
, &req
);
291 nla_put_u16(msg
, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL
,
292 net
->net_config
.keepalive
);
295 if (ep
->in
.sin_family
== AF_INET6
)
296 len
= sizeof(ep
->in6
);
298 len
= sizeof(ep
->in
);
299 nla_put(msg
, WGPEER_A_ENDPOINT
, len
, &ep
->in6
);
301 return wg_linux_peer_req_done(&req
);
304 const struct wg_ops wg_linux_ops
= {
306 .init
= wg_linux_init
,
307 .cleanup
= wg_linux_cleanup
,
308 .init_local
= wg_linux_init_local
,
309 .peer_update
= wg_linux_peer_update
,
310 .peer_refresh
= wg_linux_peer_refresh
,
311 .peer_connect
= wg_linux_peer_connect
,