# Copyright 2020 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

include <linux/net.h>
include <linux/socket.h>
include <uapi/asm-generic/socket.h>
include <uapi/linux/if.h>
include <uapi/linux/netlink.h>
include <uapi/linux/genetlink.h>
include <uapi/linux/wireguard.h>
include <drivers/net/wireguard/messages.h>

resource wireguard_ifindex[ifindex]
resource genl_wireguard_family_id[int16]

type msghdr_wireguard[CMD] msghdr_netlink[netlink_msg_t[genl_wireguard_family_id, genlmsghdr_t[CMD], device_policy]]

syz_genetlink_get_family_id$wireguard(name ptr[in, string["wireguard"]], fd sock_nl_generic) genl_wireguard_family_id

sendmsg$WG_CMD_GET_DEVICE(fd sock_nl_generic, msg ptr[in, msghdr_wireguard[WG_CMD_GET_DEVICE]], f flags[send_flags])
sendmsg$WG_CMD_SET_DEVICE(fd sock_nl_generic, msg ptr[in, msghdr_wireguard[WG_CMD_SET_DEVICE]], f flags[send_flags])

setsockopt$SO_BINDTODEVICE_wg(fd sock, level const[SOL_SOCKET], optname const[SO_BINDTODEVICE], optval ptr[in, string[wireguard_devname]], optlen len[optval])

ioctl$ifreq_SIOCGIFINDEX_wireguard(fd sock, cmd const[SIOCGIFINDEX], arg ptr[out, ifreq_dev_t[wireguard_devname, wireguard_ifindex]])

device_policy [
	WGDEVICE_A_IFINDEX	nlattr[WGDEVICE_A_IFINDEX, wireguard_ifindex]
	WGDEVICE_A_IFNAME	nlattr[WGDEVICE_A_IFNAME, string[wireguard_devname, IFNAMSIZ]]
	WGDEVICE_A_PRIVATE_KEY	nlattr[WGDEVICE_A_PRIVATE_KEY, wireguard_private_key]
	WGDEVICE_A_FLAGS	nlattr[WGDEVICE_A_FLAGS, flags[wgdevice_flag, int32]]
	WGDEVICE_A_LISTEN_PORT	nlattr[WGDEVICE_A_LISTEN_PORT, sock_port]
	WGDEVICE_A_FWMARK	nlattr[WGDEVICE_A_FWMARK, int32]
	WGDEVICE_A_PEERS	nlnest[WGDEVICE_A_PEERS, array[nlnest[0, array[peer_policy]]]]
] [varlen]

peer_policy [
	WGPEER_A_PUBLIC_KEY			nlattr[WGPEER_A_PUBLIC_KEY, wireguard_public_key]
	WGPEER_A_PRESHARED_KEY			nlattr[WGPEER_A_PRESHARED_KEY, array[int8, NOISE_SYMMETRIC_KEY_LEN]]
	WGPEER_A_FLAGS				nlattr[WGPEER_A_FLAGS, flags[wgpeer_flag, int32]]
	WGPEER_A_ENDPOINT4			nlattr[WGPEER_A_ENDPOINT, sockaddr_in]
	WGPEER_A_ENDPOINT6			nlattr[WGPEER_A_ENDPOINT, sockaddr_in6]
	WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL	nlattr[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, int16]
	WGPEER_A_ALLOWEDIPS			nlnest[WGPEER_A_ALLOWEDIPS, array[nlnest[0, array[allowedip_policy]]]]
	WGPEER_A_PROTOCOL_VERSION		nlattr[WGPEER_A_PROTOCOL_VERSION, const[1, int32]]
] [varlen]

allowedip_policy [
	ipv4	allowedip_policy$ipv4
	ipv6	allowedip_policy$ipv6
] [varlen]

allowedip_policy$ipv4 {
	WGALLOWEDIP_A_FAMILY	nlattr[WGALLOWEDIP_A_FAMILY, const[AF_INET, int16]]
	WGALLOWEDIP_A_IPADDR	nlattr[WGALLOWEDIP_A_IPADDR, ipv4_addr]
	WGALLOWEDIP_A_CIDR_MASK	nlattr[WGALLOWEDIP_A_CIDR_MASK, int8[0:3]]
} [packed]

allowedip_policy$ipv6 {
	WGALLOWEDIP_A_FAMILY	nlattr[WGALLOWEDIP_A_FAMILY, const[AF_INET6, int16]]
	WGALLOWEDIP_A_IPADDR	nlattr[WGALLOWEDIP_A_IPADDR, ipv6_addr]
	WGALLOWEDIP_A_CIDR_MASK	nlattr[WGALLOWEDIP_A_CIDR_MASK, int8[0:3]]
} [packed]

wireguard_private_key [
	zero	stringnoz[`0000000000000000000000000000000000000000000000000000000000000000`]
	a	stringnoz[`a05ca84f6c9c8e3853e2fd7a70ae0fb20fa152600cb00845174f08076f8d7843`]
	b	stringnoz[`b08073e8d44e91e3da922c22438244bb885c69e269c8e9d835b114293a4ddc6e`]
	c	stringnoz[`a0cb879a47f5bc644c0e693fa6d031c74a1553b6e901b9ff2f518c78042fb542`]
]

wireguard_public_key [
	zero	stringnoz[`0000000000000000000000000000000000000000000000000000000000000000`]
	neg	stringnoz[`dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`]
	a_g	stringnoz[`975c9d81c983c8209ee781254b899f8ed925ae9f0923c23c62f53c57cdbf691c`]
	b_g	stringnoz[`d1732899f611cd8994034d7f413dc957630e5493c285aca40065cb6311be696b`]
	c_g	stringnoz[`f44da367a88ee6564f020211456727082f5cebee8b1bf5eb7337341b459b3922`]
]

wgdevice_flag = WGDEVICE_F_REPLACE_PEERS
wgpeer_flag = WGPEER_F_REMOVE_ME, WGPEER_F_REPLACE_ALLOWEDIPS, WGPEER_F_UPDATE_ONLY
wireguard_devname = "wg0", "wg1", "wg2"

wg_packet [
	initiation	message_handshake_initiation
	response	message_handshake_response
	cookie		message_handshake_cookie
	data		message_data
] [varlen]

message_handshake_initiation {
	type			const[MESSAGE_HANDSHAKE_INITIATION, int32]
# Not clear if these indexes are also generated randomly and we need to guess them or not.
	sender_index		int32[0:4]
	unencrypted_ephemeral	array[int8, NOISE_PUBLIC_KEY_LEN]
	encrypted_static	array[int8, NOISE_PUBLIC_KEY_ENCRYPTED_LEN]
	encrypted_timestamp	array[int8, NOISE_TIMESTAMP_ENCRYPTED_LEN]
	macs			message_macs
}

message_handshake_response {
	type			const[MESSAGE_HANDSHAKE_RESPONSE, int32]
	sender_index		int32[0:4]
	receiver_index		int32[0:4]
	unencrypted_ephemeral	array[int8, NOISE_PUBLIC_KEY_LEN]
	encrypted_nothing	array[int8, NOISE_NOTHING_ENCRYPTED_LEN]
	macs			message_macs
}

message_handshake_cookie {
	type			const[MESSAGE_HANDSHAKE_COOKIE, int32]
	receiver_index		int32[0:4]
	nonce			array[int8, COOKIE_NONCE_LEN]
	encrypted_cookie	array[int8, NOISE_COOKIE_ENCRYPTED_LEN]
}

message_data {
	type		const[MESSAGE_DATA, int32]
# These are allocated randomly, so little chances guessing.
	key_idx		int32
# This is used as chacha20poly1305 decryption nonce.
	counter		int64
	encrypted_data	array[int8]
}

message_macs {
	mac1	array[int8, COOKIE_LEN]
	mac2	array[int8, COOKIE_LEN]
}

define NOISE_PUBLIC_KEY_ENCRYPTED_LEN	noise_encrypted_len(NOISE_PUBLIC_KEY_LEN)
define NOISE_TIMESTAMP_ENCRYPTED_LEN	noise_encrypted_len(NOISE_TIMESTAMP_LEN)
define NOISE_COOKIE_ENCRYPTED_LEN	noise_encrypted_len(COOKIE_LEN)
define NOISE_NOTHING_ENCRYPTED_LEN	noise_encrypted_len(0)
