# Copyright 2017 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/types.h>
include <linux/byteorder/generic.h>

syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet], frags ptr[in, vnet_fragmentation, opt])

vnet_fragmentation {
# If set and we have remaining data after fragmentation, it is written in an additional fragment.
# If not set, data remaining after fragmentation is discarded.
	full	int32[0:1]
	count	int32[1:4]
	frags	array[int32[0:4096], 4]
}

hop_limits = 0, 1, 255

resource tcp_seq_num[int32]: 0x41424344

tcp_resources {
	seq	tcp_seq_num
	ack	tcp_seq_num
}

# These pseudo syscalls read a packet from /dev/net/tun and extract tcp sequence and acknowledgement numbers from it.
# They also adds the inc arguments to the returned values, this way sequence numbers get incremented.
syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32)
syz_extract_tcp_res$synack(res ptr[out, tcp_resources], seq_inc const[1], ack_inc const[0])

################################################################################
################################### Ethernet ###################################
################################################################################

# https://en.wikipedia.org/wiki/Ethernet_frame#Structure
# https://en.wikipedia.org/wiki/IEEE_802.1Q

include <uapi/linux/if.h>
include <uapi/linux/if_ether.h>

type mac_addr_t[LAST] {
	a0	array[const[0xaa, int8], 5]
	a1	LAST
} [packed]

mac_addr_link_local {
	a0	const[0x1, int8]
	a1	const[0x80, int8]
	a2	const[0xc2, int8]
	a3	const[0x0, int8]
	a4	const[0x0, int8]
	a5	flags[mac_addr_link_local_values, int8]
} [packed]

mac_addr_link_local_values = 0x0, 0x1, 0x2, 0x3, 0xe

# This corresponds to the last digit in DEV_MAC/DEV_IPV4/DEV_IPV6 in executor/common_linux.h
type netdev_addr_id int8[10:68]

mac_addr [
	empty		array[const[0x0, int8], 6]
# These correspond to LOCAL_MAC/REMOTE_MAC/DEV_MAC in executor/common_linux.h
	local		mac_addr_t[const[0xaa, int8]]
	remote		mac_addr_t[const[0xbb, int8]]
	dev		mac_addr_t[netdev_addr_id]
	broadcast	array[const[0xff, int8], 6]
	multicast	array[const[0xbb, int8], 6]
	link_local	mac_addr_link_local
	random		array[int8, 6]
]

type mac_addr_mask array[flags[mac_addr_mask_vals, int8], 6]
mac_addr_mask_vals = 0, 0xff

vlan_tag_ad {
	tpid	const[ETH_P_8021AD, int16be]
	pcp	int16:3
	dei	int16:1
	vid	int16:12[0:4]
} [packed]

vlan_tag_q {
	tpid	const[ETH_P_8021Q, int16be]
	pcp	int16:3
	dei	int16:1
	vid	int16:12[0:4]
} [packed]

vlan_tag {
	tag_ad	optional[vlan_tag_ad]
	tag_q	vlan_tag_q
} [packed]

eth_packet {
	dst_mac	mac_addr
	src_mac	mac_addr
	vtag	optional[vlan_tag]
	payload	eth_payload
} [packed]

eth_payload {
	eth2	eth2_packet
} [packed]

################################################################################
################################## Ethernet 2 ##################################
################################################################################

# https://en.wikipedia.org/wiki/Ethernet_frame#Ethernet_II

ether_types = ETH_P_LOOP, ETH_P_PUP, ETH_P_PUPAT, ETH_P_TSN, ETH_P_IP, ETH_P_X25, ETH_P_ARP, ETH_P_IEEEPUP, ETH_P_IEEEPUPAT, ETH_P_BATMAN, ETH_P_DEC, ETH_P_DNA_DL, ETH_P_DNA_RC, ETH_P_DNA_RT, ETH_P_LAT, ETH_P_DIAG, ETH_P_CUST, ETH_P_SCA, ETH_P_TEB, ETH_P_RARP, ETH_P_ATALK, ETH_P_AARP, ETH_P_8021Q, ETH_P_ERSPAN, ETH_P_ERSPAN2, ETH_P_IPV6, ETH_P_PAUSE, ETH_P_SLOW, ETH_P_WCCP, ETH_P_MPLS_UC, ETH_P_MPLS_MC, ETH_P_ATMMPOA, ETH_P_PPP_DISC, ETH_P_PPP_SES, ETH_P_LINK_CTL, ETH_P_ATMFATE, ETH_P_PAE, ETH_P_AOE, ETH_P_8021AD, ETH_P_802_EX1, ETH_P_TIPC, ETH_P_MACSEC, ETH_P_8021AH, ETH_P_MVRP, ETH_P_1588, ETH_P_NCSI, ETH_P_PRP, ETH_P_FCOE, ETH_P_TDLS, ETH_P_FIP, ETH_P_80221, ETH_P_HSR, ETH_P_LOOPBACK, ETH_P_QINQ1, ETH_P_QINQ2, ETH_P_QINQ3, ETH_P_EDSA, ETH_P_AF_IUCV, ETH_P_802_3_MIN, ETH_P_802_3, ETH_P_AX25, ETH_P_ALL, ETH_P_802_2, ETH_P_SNAP, ETH_P_DDCMP, ETH_P_WAN_PPP, ETH_P_PPP_MP, ETH_P_LOCALTALK, ETH_P_CAN, ETH_P_CANFD, ETH_P_PPPTALK, ETH_P_TR_802_2, ETH_P_MOBITEX, ETH_P_CONTROL, ETH_P_IRDA, ETH_P_ECONET, ETH_P_HDLC, ETH_P_ARCNET, ETH_P_DSA, ETH_P_TRAILER, ETH_P_PHONET, ETH_P_IEEE802154, ETH_P_CAIF, ETH_P_XDSA, ETH_P_MAP

eth2_packet [
	generic	eth2_packet_generic
	arp	eth2_packet_t[ETH_P_ARP, arp_packet]
	ipv4	eth2_packet_t[ETH_P_IP, ipv4_packet]
	ipv6	eth2_packet_t[ETH_P_IPV6, ipv6_packet]
	llc	eth2_packet_t[ETH_P_802_2, llc_packet]
	llc_tr	eth2_packet_t[ETH_P_TR_802_2, llc_packet]
	x25	eth2_packet_t[ETH_P_X25, x25_packet]
	mpls_uc	eth2_packet_t[ETH_P_MPLS_UC, mpls_packet]
	mpls_mc	eth2_packet_t[ETH_P_MPLS_MC, mpls_packet]
	can	eth2_packet_t[ETH_P_CAN, can_frame]
	canfd	eth2_packet_t[ETH_P_CANFD, canfd_frame]
] [varlen]

eth2_packet_generic {
	etype	flags[ether_types, int16be]
	payload	array[int8]
} [packed]

type eth2_packet_t[TYPE, PAYLOAD] {
	etype	const[TYPE, int16be]
	payload	PAYLOAD
} [packed]

################################################################################
###################################### ARP #####################################
################################################################################

# https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Packet_structure

include <uapi/linux/if_arp.h>

arp_htypes = ARPHRD_NETROM, ARPHRD_ETHER, ARPHRD_EETHER, ARPHRD_AX25, ARPHRD_PRONET, ARPHRD_CHAOS, ARPHRD_IEEE802, ARPHRD_ARCNET, ARPHRD_APPLETLK, ARPHRD_DLCI, ARPHRD_ATM, ARPHRD_METRICOM, ARPHRD_IEEE1394, ARPHRD_EUI64, ARPHRD_INFINIBAND, ARPHRD_SLIP, ARPHRD_CSLIP, ARPHRD_SLIP6, ARPHRD_CSLIP6, ARPHRD_RSRVD, ARPHRD_ADAPT, ARPHRD_ROSE, ARPHRD_X25, ARPHRD_HWX25, ARPHRD_CAN, ARPHRD_PPP, ARPHRD_CISCO, ARPHRD_HDLC, ARPHRD_LAPB, ARPHRD_DDCMP, ARPHRD_RAWHDLC, ARPHRD_TUNNEL, ARPHRD_TUNNEL6, ARPHRD_FRAD, ARPHRD_SKIP, ARPHRD_LOOPBACK, ARPHRD_LOCALTLK, ARPHRD_FDDI, ARPHRD_BIF, ARPHRD_SIT, ARPHRD_IPDDP, ARPHRD_IPGRE, ARPHRD_PIMREG, ARPHRD_HIPPI, ARPHRD_ASH, ARPHRD_ECONET, ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL, ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_IEEE802154, ARPHRD_IEEE802154_MONITOR, ARPHRD_PHONET, ARPHRD_PHONET_PIPE, ARPHRD_CAIF, ARPHRD_IP6GRE, ARPHRD_NETLINK, ARPHRD_6LOWPAN, ARPHRD_VOID, ARPHRD_NONE

arp_ops = ARPOP_REQUEST, ARPOP_REPLY, ARPOP_RREQUEST, ARPOP_RREPLY, ARPOP_InREQUEST, ARPOP_InREPLY, ARPOP_NAK

arp_packet [
	generic		arp_packet_t[flags[arp_htypes, int16be], flags[ether_types, int16be], array[int8, 0:16]]
	ether_ipv4	arp_packet_t[const[ARPHRD_ETHER, int16be], const[ETH_P_IP, int16be], ipv4_addr]
	ether_ipv6	arp_packet_t[const[ARPHRD_ETHER, int16be], const[ETH_P_IPV6, int16be], ipv6_addr]
] [varlen]

type arp_packet_t[HTYPE, PTYPE, ADDR] {
	htype	HTYPE
	ptype	PTYPE
	hlen	const[6, int8]
	plen	len[spa, int8]
	op	flags[arp_ops, int16be]
	sha	mac_addr
	spa	ADDR
	tha	mac_addr
	tpa	ADDR
} [packed]

################################################################################
################################## 802.2 (LLC) #################################
################################################################################

# https://en.wikipedia.org/wiki/IEEE_802.2
# https://en.wikipedia.org/wiki/Subnetwork_Access_Protocol

include <uapi/linux/llc.h>

# Adding '1' as a SAP value since the lower bit in SAP has a special meaning.
sap_values = 1, LLC_SAP_NULL, LLC_SAP_LLC, LLC_SAP_SNA, LLC_SAP_PNM, LLC_SAP_IP, LLC_SAP_BSPAN, LLC_SAP_MMS, LLC_SAP_8208, LLC_SAP_3COM, LLC_SAP_PRO, LLC_SAP_SNAP, LLC_SAP_BANYAN, LLC_SAP_NETBEUI, LLC_SAP_LANMGR, LLC_SAP_IMPL, LLC_SAP_DISC, LLC_SAP_OSI, LLC_SAP_LAR, LLC_SAP_RM, LLC_SAP_GLOBAL

llc_generic_packet {
	dsap	flags[sap_values, int8]
	ssap	flags[sap_values, int8]
	ctrl	array[int8, 1:2]
	payload	array[int8]
} [packed]

sap_snap_values = 1, LLC_SAP_SNAP

llc_snap_packet {
	dsap		flags[sap_snap_values, int8]
	ssap		flags[sap_snap_values, int8]
	control		array[int8, 1:2]
	oui		array[int8, 3]
	protocol_id	flags[ether_types, int16be]
	payload		array[int8]
} [packed]

llc_payload [
	llc	llc_generic_packet
	snap	llc_snap_packet
] [varlen]

llc_packet {
# TODO: is there length or not? I don't see it in packet format...
#	length	len[payload, int16be]
	payload	llc_payload
} [packed]

################################################################################
###################################### x25 #####################################
################################################################################

# Documentation/networking/x25.txt
# Documentation/networking/x25-iface.txt
# http://www.dafuer.com/kleinehelferlein/x25layer.htm

include <uapi/linux/if_x25.h>
include <net/x25.h>

x25_iface_types = X25_IFACE_DATA, X25_IFACE_CONNECT, X25_IFACE_DISCONNECT, X25_IFACE_PARAMS

x25_frame_types = X25_CALL_REQUEST, X25_CALL_ACCEPTED, X25_CLEAR_REQUEST, X25_CLEAR_CONFIRMATION, X25_DATA, X25_INTERRUPT, X25_INTERRUPT_CONFIRMATION, X25_RR, X25_RNR, X25_REJ, X25_RESET_REQUEST, X25_RESET_CONFIRMATION, X25_REGISTRATION_REQUEST, X25_REGISTRATION_CONFIRMATION, X25_RESTART_REQUEST, X25_RESTART_CONFIRMATION, X25_DIAGNOSTIC, X25_ILLEGAL

x25_packet {
	iface	flags[x25_iface_types, int8]
	wtf	int8
	frame	flags[x25_frame_types, int8]
	payload	array[int8]
} [packed]

################################################################################
##################################### IPv4 #####################################
################################################################################

# https://tools.ietf.org/html/rfc791#section-3.1
# https://en.wikipedia.org/wiki/IPv4#Header

# TODO: https://en.wikipedia.org/wiki/IPsec#Authentication_Header
# TODO: https://en.wikipedia.org/wiki/IPsec#Encapsulating_Security_Payload

include <uapi/linux/in.h>
include <uapi/linux/ip.h>
include <uapi/linux/l2tp.h>
include <net/cipso_ipv4.h>
include <security/smack/smack.h>

type ipv4_addr_t[LAST] {
	a0	const[0xac, int8]
	a1	const[0x14, int8]
	a2	const[0x14, int8]
	a3	LAST
} [packed]

ipv4_addr_initdev {
	a0	const[0xac, int8]
	a1	const[0x1e, int8]
	a2	int8[0:1]
	a3	proc[1, 1, int8]
}

ipv4_addr [
# Few public random addresses 100.1.1.[0-2]
	rand_addr	int32be[0x64010100:0x64010102]
# 0.0.0.0
	empty		const[0x0, int32be]
# These correspond to LOCAL_IPV4/REMOTE_IPV4/DEV_IPV4 in executor/common_linux.h
	local		ipv4_addr_t[const[170, int8]]
	remote		ipv4_addr_t[const[187, int8]]
	dev		ipv4_addr_t[netdev_addr_id]
	initdev		ipv4_addr_initdev
# 127.0.0.1
	loopback	const[0x7f000001, int32be]
# 224.0.0.1
	multicast1	const[0xe0000001, int32be]
# 224.0.0.2
	multicast2	const[0xe0000002, int32be]
# 255.255.255.255
	broadcast	const[0xffffffff, int32be]
# 10.1.1.[0-2] can be used for custom things within the image
	private		int32be[0xa010100:0xa010102]
] [size[4]]

type ipv4_addr_mask flags[ipv4_addr_mask_vals, int32be]
ipv4_addr_mask_vals = 0, 0xff000000, 0xffffff00, 0xffffffff, 0xff

# http://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml#ip-parameters-1
ipv4_option [
	generic			ipv4_option_generic
	end			ipv4_option_end
	noop			ipv4_option_noop
	lsrr			ipv4_option_route[IPOPT_LSRR]
	ssrr			ipv4_option_route[IPOPT_SSRR]
	rr			ipv4_option_route[IPOPT_RR]
	timestamp		ipv4_option_timestamp[IPOPT_TS_TSONLY, int32be]
	timestamp_addr		ipv4_option_timestamp[IPOPT_TS_TSANDADDR, ipv4_option_timestamp_timestamp]
	timestamp_prespec	ipv4_option_timestamp[IPOPT_TS_PRESPEC, ipv4_option_timestamp_timestamp]
	cipso			ipv4_option_cipso
	ra			ipv4_option_ra
# IPOPT_SEC and IPOPT_SID are not supported by Linux kernel
] [varlen]

ipv4_option_types = IPOPT_SEC, IPOPT_LSRR, IPOPT_TIMESTAMP, IPOPT_CIPSO, IPOPT_RR, IPOPT_SID, IPOPT_SSRR, IPOPT_RA

ipv4_option_generic {
	type	flags[ipv4_option_types, int8]
	length	len[parent, int8]
	data	array[int8, 0:16]
} [packed]

# https://tools.ietf.org/html/rfc791#section-3.1
ipv4_option_end {
	type	const[IPOPT_END, int8]
} [packed]

# https://tools.ietf.org/html/rfc791#section-3.1
ipv4_option_noop {
	type	const[IPOPT_NOOP, int8]
} [packed]

# https://tools.ietf.org/html/rfc791#section-3.1
type ipv4_option_route[OPT] {
	type	const[OPT, int8]
	length	len[parent, int8]
	pointer	int8[4:255]
	data	array[ipv4_addr]
} [packed]

# https://tools.ietf.org/html/rfc791#section-3.1
# http://www.networksorcery.com/enp/protocol/ip/option004.htm
type ipv4_option_timestamp[TYP, TSTAMP] {
	type		const[IPOPT_TIMESTAMP, int8]
	length		len[parent, int8]
	pointer		int8[5:255]
	flg		const[TYP, int8:4]
	oflw		int8:4
	timestamps	array[TSTAMP]
} [packed]

ipv4_option_timestamp_timestamp {
	addr		ipv4_addr
	timestamp	int32be
} [packed]

ipv4_option_cipso_tag_types = CIPSO_V4_TAG_INVALID, CIPSO_V4_TAG_RBITMAP, CIPSO_V4_TAG_ENUM, CIPSO_V4_TAG_RANGE, CIPSO_V4_TAG_PBITMAP, CIPSO_V4_TAG_FREEFORM

# TODO: describe particular tag types
ipv4_option_cipso_tag {
	type	flags[ipv4_option_cipso_tag_types, int8]
	length	len[parent, int8]
	data	array[int8, 0:16]
} [packed]

# https://www.ietf.org/archive/id/draft-ietf-cipso-ipsecurity-01.txt
ipv4_option_cipso {
	type	const[IPOPT_CIPSO, int8]
	length	len[parent, int8]
	doi	flags[cipso_doi, int32be]
	tags	array[ipv4_option_cipso_tag]
} [packed]

cipso_doi = CIPSO_V4_DOI_UNKNOWN, SMACK_CIPSO_DOI_DEFAULT, SMACK_CIPSO_DOI_INVALID, 1, 2, 3

# https://tools.ietf.org/html/rfc2113
ipv4_option_ra {
	type	const[IPOPT_RA, int8]
	length	len[parent, int8]
	value	bool16
} [packed]

ipv4_options {
	options	array[ipv4_option]
} [packed, align[4]]

ipv4_types = IPPROTO_IP, IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_TCP, IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_TP, IPPROTO_DCCP, IPPROTO_IPV6, IPPROTO_RSVP, IPPROTO_GRE, IPPROTO_ESP, IPPROTO_AH, IPPROTO_MTP, IPPROTO_BEETPH, IPPROTO_ENCAP, IPPROTO_PIM, IPPROTO_COMP, IPPROTO_SCTP, IPPROTO_UDPLITE, IPPROTO_MPLS, IPPROTO_RAW, IPPROTO_L2TP

type ipv4_header[PROTO] {
	ihl		bytesize4[parent, int8:4]
	version		const[4, int8:4]
	ecn		int8:2
	dscp		int8:6
# TODO: if s/ipv4_packet_t/ipv4_packet/, (1) this crashes, (2) only at runtime with:
# panic: len field "total_len" references non existent field "ipv4_packet", pos="len"/"total_len"
	total_len	len[ipv4_packet_t, int16be]
	id		int16be[100:104]
# TODO: frag_off is actually 13 bits, 3 bits are flags
	frag_off	int16be[0:0]
	ttl		int8
	protocol	PROTO
	csum		csum[parent, inet, int16be]
	src_ip		ipv4_addr
	dst_ip		ipv4_addr
	options		ipv4_options
} [packed]

type ipv4_packet_t[PROTO, PAYLOAD] {
	header	ipv4_header[PROTO]
	payload	PAYLOAD
} [packed]

ipv4_packet [
	generic	ipv4_packet_t[flags[ipv4_types, int8], array[int8]]
	tcp	ipv4_packet_t[const[IPPROTO_TCP, int8], tcp_packet]
	udp	ipv4_packet_t[const[IPPROTO_UDP, int8], udp_packet]
	icmp	ipv4_packet_t[const[IPPROTO_ICMP, int8], icmp_packet]
	dccp	ipv4_packet_t[const[IPPROTO_DCCP, int8], dccp_packet]
	igmp	ipv4_packet_t[const[IPPROTO_IGMP, int8], igmp_packet]
	gre	ipv4_packet_t[const[IPPROTO_GRE, int8], gre_packet]
	esp	ipv4_packet_t[const[IPPROTO_ESP, int8], esp_aggfrag_packet]
# TODO: what proto do we need for tipc (there is no IPPROTO_TIPC)?
	tipc	ipv4_packet_t[const[IPPROTO_TCP, int8], tipc_packet]
] [varlen]

################################################################################
##################################### IPv6 #####################################
################################################################################

# https://tools.ietf.org/html/rfc2460#section-3
# https://en.wikipedia.org/wiki/IPv6_packet#Fixed_header

include <uapi/linux/in6.h>
include <uapi/linux/ipv6.h>
include <uapi/linux/seg6.h>
include <uapi/linux/ip6_tunnel.h>
include <net/ipv6.h>

ipv6_types = IPPROTO_IP, IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_TCP, IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_TP, IPPROTO_DCCP, IPPROTO_IPV6, IPPROTO_RSVP, IPPROTO_GRE, IPPROTO_ESP, IPPROTO_AH, IPPROTO_MTP, IPPROTO_BEETPH, IPPROTO_ENCAP, IPPROTO_PIM, IPPROTO_COMP, IPPROTO_SCTP, IPPROTO_UDPLITE, IPPROTO_MPLS, IPPROTO_RAW, IPPROTO_HOPOPTS, IPPROTO_ROUTING, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_NONE, IPPROTO_DSTOPTS, IPPROTO_MH, NEXTHDR_HOP, NEXTHDR_ROUTING, NEXTHDR_FRAGMENT, NEXTHDR_GRE, NEXTHDR_ESP, NEXTHDR_AUTH, NEXTHDR_ICMP, NEXTHDR_NONE, NEXTHDR_DEST, NEXTHDR_MOBILITY, IPPROTO_L2TP

ipv6_addr_empty {
	a0	array[const[0x0, int8], 16]
} [packed, align[4]]

type ipv6_addr_t[LAST] {
	a0	const[0xfe, int8]
	a1	const[0x80, int8]
	a2	array[const[0x0, int8], 13]
	a3	LAST
} [packed, align[4]]

ipv6_addr_initdev {
	a0	const[0xfe, int8]
	a1	const[0x88, int8]
	a2	array[const[0x0, int8], 12]
	a3	int8[0:1]
	a4	proc[1, 1, int8]
} [packed, align[4]]

ipv6_addr_loopback {
	a0	const[0, int64be]
	a1	const[1, int64be]
} [packed, align[4]]

ipv6_addr_ipv4 {
	a0	array[const[0x0, int8], 10]
	a1	array[const[0xff, int8], 2]
	a3	ipv4_addr
} [packed, align[4]]

ipv6_addr_multicast1 {
	a0	const[0xff, int8]
	a1	const[0x1, int8]
	a2	array[const[0x0, int8], 13]
	a3	const[0x1, int8]
} [packed, align[4]]

ipv6_addr_multicast2 {
	a0	const[0xff, int8]
	a1	const[0x2, int8]
	a2	array[const[0x0, int8], 13]
	a3	const[0x1, int8]
} [packed, align[4]]

type ipv6_addr_private[BYTE2] {
	a0	const[0xfc, int8]
	a1	const[BYTE2, int8]
	a2	array[const[0x0, int8], 13]
	a3	int8[0:1]
} [packed, align[4]]

ipv6_addr_random = `20010000000000000000000000000000`, `20010000000000000000000000000001`, `20010000000000000000000000000002`

ipv6_addr [
# Few public random addresses
	rand_addr	stringnoz[ipv6_addr_random]
	empty		ipv6_addr_empty
# These correspond to LOCAL_IPV6/REMOTE_IPV6/DEV_IPV6 in executor/common_linux.h
	local		ipv6_addr_t[const[0xaa, int8]]
	remote		ipv6_addr_t[const[0xbb, int8]]
	dev		ipv6_addr_t[netdev_addr_id]
	initdev		ipv6_addr_initdev
# Some special addresses:
	loopback	ipv6_addr_loopback
	ipv4		ipv6_addr_ipv4
	mcast1		ipv6_addr_multicast1
	mcast2		ipv6_addr_multicast2
# Several custom private ranges with 2 addresses each: fc0X::/127.
# Can be used for custom things within the tested image.
	private0	ipv6_addr_private[0]
	private1	ipv6_addr_private[1]
	private2	ipv6_addr_private[2]
] [size[16]]

type ipv6_addr_mask array[flags[ipv4_addr_mask_vals, int32be], 4]

# TODO: Describe more types of headers
# NEXTHDR_HOP, NEXTHDR_TCP, NEXTHDR_UDP, NEXTHDR_IPV6, NEXTHDR_FRAGMENT, NEXTHDR_GRE, NEXTHDR_ESP, NEXTHDR_AUTH, NEXTHDR_ICMP, NEXTHDR_NONE, NEXTHDR_DEST, NEXTHDR_SCTP, NEXTHDR_MOBILITY
# https://tools.ietf.org/html/rfc2402
# https://tools.ietf.org/html/rfc2406
# https://tools.ietf.org/html/rfc3775

# https://tools.ietf.org/html/rfc2460#section-4
# The length field in each of the extension headers specifies the
# length of the header in 8-octet units not including the first 8 octets.
ipv6_ext_header [
	hopopts		ipv6_hopopts_ext_header
	routing		ipv6_rt_hdr
	srh		ipv6_sr_hdr
	fragment	ipv6_fragment_ext_header
	dstopts		ipv6_dstopts_ext_header
] [varlen]

ipv6_hopopts_ext_header {
	next_header	flags[ipv6_types, int8]
	length		bytesize8[options, int8]
	pad		array[const[0, int8], 6]
	options		array[ipv6_tlv_option]
} [packed, align[8]]

ipv6_routing_types = IPV6_SRCRT_STRICT, IPV6_SRCRT_TYPE_0, IPV6_SRCRT_TYPE_2

ipv6_rt_hdr {
	next_header	flags[ipv6_types, int8]
	length		bytesize8[data, int8]
	routing_type	flags[ipv6_routing_types, int8]
	segments_left	int8
	reserved	const[0, int32]
	data		array[ipv6_addr]
} [packed, align[8]]

ipv6_sr_hdr {
	nexthdr		flags[ipv6_types, int8]
	hdrlen		bytesize8[segments, int8]
	type		const[IPV6_SRCRT_TYPE_4, int8]
	segments_left	len[segments, int8]
	first_segment	int8
	flags		flags[ipv6_sr_flags, int8]
	tag		int16
	segments	array[ipv6_addr]
# TODO: this may be followed by sr6_tlv_hmac if SR6_FLAG1_HMAC is set.
# However, if we place it here, we won't be able to calculate hdrlen (len of 2 fields),
# and if we move segments and sr6_tlv_hmac into a separate struct,
# we won't be able to calculate segments_left because it will need to
# refer to a field of a subobject. What may help is allowing specifying
# subfields as len/bytesize targets, e.g. "len[payload.segments]", or "bytesize[parent_struct.foo]".
} [packed, align[8]]

ipv6_sr_flags = SR6_FLAG1_PROTECTED, SR6_FLAG1_OAM, SR6_FLAG1_ALERT, SR6_FLAG1_HMAC

ipv6_fragment_ext_header {
	next_header	flags[ipv6_types, int8]
	reserved1	const[0, int8]
	fragment_off_hi	int8
	m_flag		int8:1
	reserved2	const[0, int8:2]
	fragment_off_lo	int8:5
	identification	int32[100:104]
} [packed, align[8]]

ipv6_dstopts_ext_header {
	next_header	flags[ipv6_types, int8]
	length		bytesize8[options, int8]
	pad		array[const[0, int8], 6]
	options		array[ipv6_tlv_option]
} [packed, align[8]]

ipv6_tlv_option [
	generic	ipv6_tlv_generic
	pad1	ipv6_tlv_pad1
	padn	ipv6_tlv_padn
	ra	ipv6_tlv_ra
	jumbo	ipv6_tlv_jumbo
	calipso	ipv6_tlv_calipso
	hao	ipv6_tlv_hao
	enc_lim	ipv6_tlv_enc_lim
] [varlen]

ipv6_tlv_generic {
	type	int8
	length	len[data, int8]
	data	array[int8]
} [packed]

ipv6_tlv_pad1 {
	type	const[IPV6_TLV_PAD1, int8]
	len	const[1, int8]
	pad	const[0, int8]
} [packed]

ipv6_tlv_padn {
	type	const[IPV6_TLV_PADN, int8]
	len	len[pad, int8]
	pad	array[const[0, int8]]
} [packed]

ipv6_tlv_ra {
	type	const[IPV6_TLV_ROUTERALERT, int8]
	len	const[2, int8]
	ra	int16be
} [packed]

ipv6_tlv_jumbo {
	type	const[IPV6_TLV_JUMBO, int8]
	len	const[4, int8]
	pkt_len	int32be
} [packed]

# https://tools.ietf.org/html/rfc5570#section-5.1
ipv6_tlv_calipso {
	type	const[IPV6_TLV_CALIPSO, int8]
	len	bytesize[payload, int8]
	payload	ipv6_tlv_calipso_payload
} [packed]

# TODO: checksum is generally incorrect.
ipv6_tlv_calipso_payload {
	domain			flags[calipso_doi, int32be]
	compartment_length	bytesize4[compartment_bitmap, int8]
	sensitivity_level	int8
	checksum		int16
	compartment_bitmap	array[int64]
} [packed]

calipso_doi = 0, 1, 2, 3

ipv6_tlv_hao {
	type	const[IPV6_TLV_HAO, int8]
	len	bytesize[addr, int8]
	addr	ipv6_addr
} [packed]

ipv6_tlv_enc_lim {
	type		const[IPV6_TLV_TNL_ENCAP_LIMIT, int8]
	len		const[1, int8]
	encap_limit	int8
} [packed]

ipv6_packet [
	generic		ipv6_packet_t[flags[ipv6_types, int8], array[int8]]
	tcp		ipv6_packet_t[const[IPPROTO_TCP, int8], tcp_packet]
	udp		ipv6_packet_t[const[IPPROTO_UDP, int8], udp_packet]
	icmpv6		ipv6_packet_t[const[IPPROTO_ICMPV6, int8], icmpv6_packet]
	dccp_packet	ipv6_packet_t[const[IPPROTO_DCCP, int8], dccp_packet]
	gre_packet	ipv6_packet_t[const[IPPROTO_GRE, int8], gre_packet]
	esp		ipv6_packet_t[const[IPPROTO_ESP, int8], esp_aggfrag_packet]
# TODO: what proto do we need for tipc (there is no IPPROTO_TIPC)?
	tipc_packet	ipv6_packet_t[const[IPPROTO_TCP, int8], tipc_packet]
] [varlen]

type ipv6_packet_t[PROTO, PAYLOAD] {
	priority	int8:4
	version		const[6, int8:4]
# TODO: flow_label is actually 20 bits, 4 bits are part of priority
	flow_label	array[int8, 3]
	length		len[payload, int16be]
	next_header	PROTO
	hop_limit	flags[hop_limits, int8]
	src_ip		ipv6_addr
	dst_ip		ipv6_addr
	payload		ipv6_packet_payload[PAYLOAD]
} [packed]

type ipv6_packet_payload[PAYLOAD] {
	ext_headers	array[ipv6_ext_header]
	payload		PAYLOAD
} [packed]

################################################################################
###################################### TCP #####################################
################################################################################

# https://tools.ietf.org/html/rfc793#section-3.1
# https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure
# http://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml

include <net/tcp.h>
include <uapi/linux/tcp.h>

tcp_option [
	generic		tcp_generic_option
	nop		tcp_nop_option
	eol		tcp_eol_option
	mss		tcp_mss_option
	window		tcp_window_option
	sack_perm	tcp_sack_perm_option
	sack		tcp_sack_option
	timestamp	tcp_timestamp_option
	md5sig		tcp_md5sig_option
	fastopen	tcp_fastopen_option
	exp_fastopen	tcp_exp_fastopen_option
	exp_smc		tcp_exp_smc_option
	mptcp		tcp_mptcp_option
] [varlen]

tcp_option_types = TCPOPT_NOP, TCPOPT_EOL, TCPOPT_MSS, TCPOPT_WINDOW, TCPOPT_SACK_PERM, TCPOPT_SACK, TCPOPT_TIMESTAMP, TCPOPT_MD5SIG, TCPOPT_FASTOPEN, TCPOPT_EXP

tcp_generic_option {
	type	flags[tcp_option_types, int8]
	length	len[parent, int8]
	data	array[int8, 0:16]
} [packed]

# https://tools.ietf.org/html/rfc793#section-3.1
tcp_nop_option {
	type	const[TCPOPT_NOP, int8]
} [packed]

# https://tools.ietf.org/html/rfc793#section-3.1
tcp_eol_option {
	type	const[TCPOPT_EOL, int8]
} [packed]

# https://tools.ietf.org/html/rfc793#section-3.1
tcp_mss_option {
	type		const[TCPOPT_MSS, int8]
	length		len[parent, int8]
	seg_size	int16
} [packed]

# https://tools.ietf.org/html/rfc7323#section-2
tcp_window_option {
	type	const[TCPOPT_WINDOW, int8]
	length	len[parent, int8]
	shift	int8
} [packed]

# https://tools.ietf.org/html/rfc2018#section-2
tcp_sack_perm_option {
	type	const[TCPOPT_SACK_PERM, int8]
	length	len[parent, int8]
} [packed]

# https://tools.ietf.org/html/rfc2018#section-3
tcp_sack_option {
	type	const[TCPOPT_SACK, int8]
	length	len[parent, int8]
	data	array[int32be]
} [packed]

# https://tools.ietf.org/html/rfc7323#section-3
tcp_timestamp_option {
	type	const[TCPOPT_TIMESTAMP, int8]
	length	len[parent, int8]
	tsval	int32be
	tsecr	int32be
} [packed]

# https://tools.ietf.org/html/rfc2385#section-3.0
tcp_md5sig_option {
	type	const[TCPOPT_MD5SIG, int8]
	length	len[parent, int8]
	md5	array[int8, 16]
} [packed]

# https://tools.ietf.org/html/rfc7413#section-4.1.1
tcp_fastopen_option {
	type	const[TCPOPT_FASTOPEN, int8]
	length	len[parent, int8]
	data	array[int8, 0:16]
} [packed]

tcp_exp_fastopen_option {
	type	const[TCPOPT_EXP, int8]
	length	len[parent, int8]
	subtype	const[TCPOPT_FASTOPEN_MAGIC, int16be]
	data	array[int8, 0:16]
} [packed]

tcp_exp_smc_option {
	type	const[TCPOPT_EXP, int8]
	length	len[parent, int8]
	subtype	const[TCPOPT_SMC_MAGIC, int32be]
} [packed]

tcp_options {
	options	array[tcp_option]
} [packed, align[4]]

tcp_flags = TCPHDR_FIN, TCPHDR_SYN, TCPHDR_RST, TCPHDR_PSH, TCPHDR_ACK, TCPHDR_URG, TCPHDR_ECE, TCPHDR_CWR, TCPHDR_SYN_ECN

tcp_header {
	src_port	sock_port
	dst_port	sock_port
	seq_num		tcp_seq_num
	ack_num		tcp_seq_num
	ns		int8:1
	reserved	const[0, int8:3]
	data_off	bytesize4[parent, int8:4]
	flags		flags[tcp_flags, int8]
	window_size	int16be
	csum		csum[tcp_packet, pseudo, IPPROTO_TCP, int16be]
	urg_ptr		int16be
	options		tcp_options
} [packed]

tcp_packet {
	header	tcp_header
	payload	tcp_payload
} [packed]

tcp_payload {
	payload	array[int8]
} [packed]

################################################################################
###################################### UDP #####################################
################################################################################

# https://tools.ietf.org/html/rfc768
# https://en.wikipedia.org/wiki/User_Datagram_Protocol#Packet_structure

include <net/gue.h>

udp_packet {
	src_port	sock_port
	dst_port	sock_port
	length		len[parent, int16be]
	csum		csum[parent, pseudo, IPPROTO_UDP, int16be]
	payload		udp_payload
} [packed]

udp_payload [
	opaque	array[int8]
	gue	gue_packet
	wg	wg_packet
] [varlen]

gue_packet {
	hdr	guehdr
	opaque	array[int8]
} [packed]

guehdr {
	hlen		bytesize4[parent, int8:5]
	control		int8:1
	version		int8:2
	proto_ctype	int8
	flags		flags[guehdr_flags, int16]
	priv		optional[flags[guehdr_prov_flags, int32]]
} [packed]

guehdr_flags = GUE_FLAG_PRIV
guehdr_prov_flags = GUE_PFLAG_REMCSUM

################################################################################
###################################### GRE #####################################
################################################################################

# https://en.wikipedia.org/wiki/Generic_Routing_Encapsulation

include <net/gre.h>

gre_packet {
	pptp		gre_packet_pptp
# TODO: add more packets
# TODO: the payload should be ipv4_packet/ipv6_packet, but this creates recursion
# ipv4 -> gre -> ipv4 -> ...
	cisco_ipv4	gre_packet_cisco[ETH_P_IP, array[int8]]
	cisco_ipv6	gre_packet_cisco[ETH_P_IPV6, array[int8]]
	erspan1		gre_packet_erspan[ETH_P_ERSPAN, erspan_md1_msg]
	erspan2		gre_packet_erspan[ETH_P_ERSPAN2, erspan_md2_msg]
	teb		gre_packet_erspan[ETH_P_TEB, array[int8]]
} [packed]

type gre_packet_cisco[PROTO, PAYLOAD] {
	C		int16:1
	R		const[0, int16:1]
	K		int16:1
	S		int16:1
	reserved	const[0, int16:9]
	version		const[0, int16:3]
	protocol	const[PROTO, int16be]
# checksum, key, sequence number
	add		array[int16be, 0:3]
	payload		PAYLOAD
} [packed]

gre_packet_pptp {
	C		const[0, int16:1]
	R		const[0, int16:1]
	K		const[1, int16:1]
	S		int16:1
	reserved	const[0, int16:4]
	A		int16:1
	flags		const[0, int16:4]
	version		const[1, int16:3]
	protocol	const[0x880b, int16be]
	payload_len	bytesize[payload, int16be]
	key_call_id	pptp_call_id
# sequence/ack number
	add		array[int16be, 0:2]
	payload		ppp_packet
} [packed]

type ppp_packet array[int8]

type gre_packet_erspan[PROTO, PAYLOAD] {
	H		const[8, int16]
	protocol	const[PROTO, int16be]
	seq		int32be[0:4]
	payload		PAYLOAD
} [packed]

################################################################################
##################################### ERSPAN ###################################
################################################################################

include <net/erspan.h>
include <uapi/linux/erspan.h>

type erspan_base_hdr[VER] {
	vlan_upper		int8:4
	ver			const[VER, int8:4]
	vlan			int8:8
	session_id_upper	int8:2
	t			int8:1
	en			int8:2
	cos			int8:3
	session_id		int8:8
} [packed]

erspan_md1 {
	index	int32be
} [packed]

erspan_md1_msg {
	base	erspan_base_hdr[1]
	version	const[1, int32]
	payload	erspan_md1
}

erspan_md2 {
	timestamp	int32be
	sgt		int16be
	hwid_upper	int8:2
	ft		int8:5
	p		int8:1
	o		int8:1
	gra		int8:2
	dir		int8:1
	hwid		int8:1
} [packed]

erspan_md2_msg {
	base	erspan_base_hdr[2]
	version	const[2, int32]
	payload	erspan_md2
}

################################################################################
###################################### ICMP ####################################
################################################################################

# https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#ICMP_datagram_structure
# https://tools.ietf.org/html/rfc792
# https://tools.ietf.org/html/rfc4884#section-4.1
# http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml

include <uapi/linux/icmp.h>

icmp_ipv4_header {
	ihl		bytesize4[parent, int8:4]
	version		const[4, int8:4]
	ecn		int8:2
	dscp		int8:6
	total_len	int16be
	id		icmp_id
	frag_off	int16be
	ttl		int8
	protocol	flags[ipv4_types, int8]
	csum		int16be
	src_ip		ipv4_addr
	dst_ip		ipv4_addr
	options		ipv4_options
} [packed]

icmp_echo_reply_packet {
	type	const[ICMP_ECHOREPLY, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	id	icmp_id
	seq_num	int16be
	data	array[int8]
} [packed]

type icmp_id int16be[100:104]

icmp_dest_unreach_codes = ICMP_NET_UNREACH, ICMP_HOST_UNREACH, ICMP_PROT_UNREACH, ICMP_PORT_UNREACH, ICMP_FRAG_NEEDED, ICMP_SR_FAILED, ICMP_NET_UNKNOWN, ICMP_HOST_UNKNOWN, ICMP_HOST_ISOLATED, ICMP_NET_ANO, ICMP_HOST_ANO, ICMP_NET_UNR_TOS, ICMP_HOST_UNR_TOS, ICMP_PKT_FILTERED, ICMP_PREC_VIOLATION, ICMP_PREC_CUTOFF

icmp_dest_unreach_packet {
	type	const[ICMP_DEST_UNREACH, int8]
	code	flags[icmp_dest_unreach_codes, int8]
	csum	csum[parent, inet, int16be]
	unused	const[0, int8]
	length	int8
	mtu	int16be
	iph	icmp_ipv4_header
	data	array[int8, 0:8]
} [packed]

icmp_source_quench_packet {
	type	const[ICMP_SOURCE_QUENCH, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	unused	const[0, int32]
	iph	icmp_ipv4_header
	data	array[int8, 0:8]
} [packed]

icmp_redirect_codes = ICMP_REDIR_NET, ICMP_REDIR_HOST, ICMP_REDIR_NETTOS, ICMP_REDIR_HOSTTOS

icmp_redirect_packet {
	type	const[ICMP_REDIRECT, int8]
	code	flags[icmp_redirect_codes, int8]
	csum	csum[parent, inet, int16be]
	ip	ipv4_addr
	iph	icmp_ipv4_header
	data	array[int8, 0:8]
} [packed]

icmp_echo_packet {
	type	const[ICMP_ECHO, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	id	int16be
	seq_num	int16be
	data	array[int8]
} [packed]

icmp_time_exceeded_codes = ICMP_EXC_TTL, ICMP_EXC_FRAGTIME

icmp_time_exceeded_packet {
	type	const[ICMP_TIME_EXCEEDED, int8]
	code	flags[icmp_time_exceeded_codes, int8]
	csum	csum[parent, inet, int16be]
	unused1	const[0, int8]
	length	int8
	unused2	const[0, int16]
	iph	icmp_ipv4_header
	data	array[int8, 0:8]
} [packed]

icmp_parameter_prob_packet {
	type	const[ICMP_PARAMETERPROB, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	pointer	int8
	length	int8
	unused	const[0, int16]
	iph	icmp_ipv4_header
	data	array[int8, 0:8]
} [packed]

icmp_timestamp_packet {
	type		const[ICMP_TIMESTAMP, int8]
	code		const[0, int8]
	csum		csum[parent, inet, int16be]
	id		int16be
	seq_num		int16be
	orig_ts		int32be
	recv_ts		int32be
	trans_ts	int32be
} [packed]

icmp_timestamp_reply_packet {
	type		const[ICMP_TIMESTAMPREPLY, int8]
	code		const[0, int8]
	csum		csum[parent, inet, int16be]
	id		int16be
	seq_num		int16be
	orig_ts		int32be
	recv_ts		int32be
	trans_ts	int32be
} [packed]

icmp_info_request_packet {
	type	const[ICMP_INFO_REQUEST, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	id	int16be
	seq_num	int16be
} [packed]

icmp_info_reply_packet {
	type	const[ICMP_INFO_REPLY, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	id	int16be
	seq_num	int16be
} [packed]

icmp_address_request_packet {
	type	const[ICMP_ADDRESS, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	mask	int32be
} [packed]

icmp_address_reply_packet {
	type	const[ICMP_ADDRESSREPLY, int8]
	code	const[0, int8]
	csum	csum[parent, inet, int16be]
	mask	int32be
} [packed]

icmp_types = ICMP_ECHOREPLY, ICMP_DEST_UNREACH, ICMP_SOURCE_QUENCH, ICMP_REDIRECT, ICMP_ECHO, ICMP_TIME_EXCEEDED, ICMP_PARAMETERPROB, ICMP_TIMESTAMP, ICMP_TIMESTAMPREPLY, ICMP_INFO_REQUEST, ICMP_INFO_REPLY, ICMP_ADDRESS, ICMP_ADDRESSREPLY

icmp_packet [
	echo_reply	icmp_echo_reply_packet
	dest_unreach	icmp_dest_unreach_packet
	source_quench	icmp_source_quench_packet
	redirect	icmp_redirect_packet
	echo		icmp_echo_packet
	time_exceeded	icmp_time_exceeded_packet
	parameter_prob	icmp_parameter_prob_packet
	timestamp	icmp_timestamp_packet
	timestamp_reply	icmp_timestamp_reply_packet
	info_request	icmp_info_request_packet
	info_reply	icmp_info_reply_packet
	address_request	icmp_address_request_packet
	address_reply	icmp_address_reply_packet
] [varlen]

################################################################################
##################################### ICMPv6 ###################################
################################################################################

# https://tools.ietf.org/html/rfc4443
# http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml

include <uapi/linux/icmpv6.h>

icmpv6_ipv6_packet {
	priority	int8:4
	version		const[6, int8:4]
	flow_label	array[int8, 3]
	length		int16be
	next_header	flags[ipv6_types, int8]
	hop_limit	flags[hop_limits, int8]
	src_ip		ipv6_addr
	dst_ip		ipv6_addr
	ext_headers	array[ipv6_ext_header]
	data		array[int8]
} [packed]

icmpv6_dest_unreach_codes = ICMPV6_NOROUTE, ICMPV6_ADM_PROHIBITED, ICMPV6_NOT_NEIGHBOUR, ICMPV6_ADDR_UNREACH, ICMPV6_PORT_UNREACH, ICMPV6_POLICY_FAIL, ICMPV6_REJECT_ROUTE

icmpv6_dest_unreach_packet {
	type	const[ICMPV6_DEST_UNREACH, int8]
	code	flags[icmpv6_dest_unreach_codes, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	length	int8
	unused	array[const[0, int8], 3]
	packet	icmpv6_ipv6_packet
} [packed]

icmpv6_pkt_toobig_packet {
	type	const[ICMPV6_PKT_TOOBIG, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	mtu	int32be
	packet	icmpv6_ipv6_packet
} [packed]

icmpv6_time_exceed_codes = ICMPV6_EXC_HOPLIMIT, ICMPV6_EXC_FRAGTIME

icmpv6_time_exceed_packet {
	type	const[ICMPV6_TIME_EXCEED, int8]
	code	flags[icmpv6_time_exceed_codes, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	length	int8
	unused	array[const[0, int8], 3]
	packet	icmpv6_ipv6_packet
} [packed]

icmpv6_param_prob_codes = ICMPV6_HDR_FIELD, ICMPV6_UNK_NEXTHDR, ICMPV6_UNK_OPTION

icmpv6_param_prob_packet {
	type	const[ICMPV6_PARAMPROB, int8]
	code	flags[icmpv6_param_prob_codes, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	pointer	int32be
	packet	icmpv6_ipv6_packet
} [packed]

icmpv6_echo_request_packet {
	type	const[ICMPV6_ECHO_REQUEST, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	id	int16be
	seq_num	int16be
	data	array[int8]
} [packed]

icmpv6_echo_reply_packet {
	type	const[ICMPV6_ECHO_REPLY, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	id	int16be
	seq_num	int16be
	data	array[int8]
} [packed]

icmpv6_mld_types = ICMPV6_MGM_QUERY, ICMPV6_MGM_REPORT, ICMPV6_MGM_REDUCTION

# https://tools.ietf.org/html/rfc2710#section-3
icmpv6_mld_packet {
	type	flags[icmpv6_mld_types, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	mrd	int16be
	unused	int16
	addr	ipv6_addr
} [packed]

# https://tools.ietf.org/html/rfc3810#section-5.1
icmpv6_mldv2_listener_query_packet {
	type		const[ICMPV6_MGM_QUERY, int8]
	code		const[0, int8]
	csum		csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	mrd		int16be
	unused		int16
	mca		ipv6_addr
	qrv		int8:3
	suppress	int8:1
	resv2		int8:4
	qqic		int8
	nsrcs		len[srcs, int16be]
	srcs		array[ipv6_addr]
} [packed]

icmpv6_mldv2_grec {
	type		int8
	auxwords	len[aux, int8]
	nsrcs		len[srcs, int16be]
	mca		ipv6_addr
	srcs		array[ipv6_addr]
	aux		array[int32]
} [packed]

# https://tools.ietf.org/html/rfc3810#section-5.2
icmpv6_mldv2_listener_report_packet {
	type	const[ICMPV6_MLD2_REPORT, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	unused	int16
	ngrec	len[grec, int16be]
	grec	array[icmpv6_mldv2_grec]
} [packed]

icmpv6_ni_types = ICMPV6_NI_QUERY, ICMPV6_NI_REPLY

# https://tools.ietf.org/html/rfc4620#section-4
icmpv6_ni_packet {
	type	flags[icmpv6_ni_types, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	qtype	int16be
	flags	int16be
	nonce	int64be
	data	array[int8]
} [packed]

icmpv6_ndisc_option_types = ND_OPT_SOURCE_LL_ADDR, ND_OPT_TARGET_LL_ADDR, ND_OPT_PREFIX_INFO, ND_OPT_REDIRECT_HDR, ND_OPT_MTU, ND_OPT_NONCE, ND_OPT_ROUTE_INFO, ND_OPT_RDNSS, ND_OPT_DNSSL, ND_OPT_6CO

# https://tools.ietf.org/html/rfc4861#section-4.6
icmpv6_ndisc_option {
	option_type	flags[icmpv6_ndisc_option_types, int8]
	length		bytesize8[parent, int8]
# TODO: define the option formats
	data		array[int8]
} [packed]

# https://tools.ietf.org/html/rfc4861#section-4.1
icmpv6_ndisc_router_solicit_packet {
	type	const[NDISC_ROUTER_SOLICITATION, int8]
	code	const[0, int8]
	csum	csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	unused	array[const[0, int8], 4]
	options	array[icmpv6_ndisc_option]
} [packed]

# https://tools.ietf.org/html/rfc4861#section-4.2
icmpv6_ndisc_router_advert_packet {
	type		const[NDISC_ROUTER_ADVERTISEMENT, int8]
	code		const[0, int8]
	csum		csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	cur_hop_limit	flags[hop_limits, int8]
# TODO: Implement bitflags for the router advert flags
	router_flags	int8
	router_lifetime	int16
	reachable_time	int32
	retrans_time	int32
	options		array[icmpv6_ndisc_option]
} [packed]

# https://tools.ietf.org/html/rfc4861#section-4.3
icmpv6_ndisc_neigh_solicit_packet {
	type		const[NDISC_NEIGHBOUR_SOLICITATION, int8]
	code		const[0, int8]
	csum		csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	target_addr	ipv6_addr
	options		array[icmpv6_ndisc_option]
} [packed]

# https://tools.ietf.org/html/rfc4861#section-4.4
icmpv6_ndisc_neigh_advert_packet {
	type		const[NDISC_NEIGHBOUR_ADVERTISEMENT, int8]
	code		const[0, int8]
	csum		csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
# TODO: Implement bitflags for the neighbor advert flags
	neighbor_flags	int8
	unused		array[const[0, int8], 3]
	target_addr	ipv6_addr
	options		array[icmpv6_ndisc_option]
} [packed]

# https://tools.ietf.org/html/rfc4861#section-4.5
icmpv6_ndisc_redir_packet {
	type		const[NDISC_REDIRECT, int8]
	code		const[0, int8]
	csum		csum[parent, pseudo, IPPROTO_ICMPV6, int16be]
	unused		array[const[0, int8], 4]
	target_addr	ipv6_addr
	dst_addr	ipv6_addr
	options		array[icmpv6_ndisc_option]
} [packed]

icmpv6_packet [
	dest_unreach	icmpv6_dest_unreach_packet
	pkt_toobig	icmpv6_pkt_toobig_packet
	time_exceed	icmpv6_time_exceed_packet
	param_prob	icmpv6_param_prob_packet
	echo_request	icmpv6_echo_request_packet
	echo_reply	icmpv6_echo_reply_packet
	mld		icmpv6_mld_packet
	mlv2_query	icmpv6_mldv2_listener_query_packet
	mlv2_report	icmpv6_mldv2_listener_report_packet
	ni		icmpv6_ni_packet
	ndisc_rs	icmpv6_ndisc_router_solicit_packet
	ndisc_ra	icmpv6_ndisc_router_advert_packet
	ndisc_na	icmpv6_ndisc_neigh_advert_packet
	ndisc_ns	icmpv6_ndisc_neigh_solicit_packet
	ndisc_redir	icmpv6_ndisc_redir_packet
# TODO: ICMPV6_DHAAD_REQUEST, ICMPV6_DHAAD_REPLY, ICMPV6_MOBILE_PREFIX_SOL, ICMPV6_MOBILE_PREFIX_ADV (with ipv6 ext headers)
] [varlen]

################################################################################
###################################### DCCP ####################################
################################################################################

# https://tools.ietf.org/html/rfc4340#section-5

include <uapi/linux/dccp.h>

# TODO: describe each type
dccp_types = DCCP_PKT_REQUEST, DCCP_PKT_RESPONSE, DCCP_PKT_DATA, DCCP_PKT_ACK, DCCP_PKT_DATAACK, DCCP_PKT_CLOSEREQ, DCCP_PKT_CLOSE, DCCP_PKT_RESET, DCCP_PKT_SYNC, DCCP_PKT_SYNCACK, DCCP_PKT_INVALID

dccp_header {
	src_port	sock_port
	dst_port	sock_port
	offset		bytesize4[parent, int8]
	cscov		const[1, int8:4]
# TODO: cscov might have other values, affects checksummed data
	ccval		int8:4
	csum		csum[parent, pseudo, IPPROTO_DCCP, int16be]
	x		const[0, int8:1]
	type		flags[dccp_types, int8:4]
	reserved1	int8:3
	seq_num		array[int8, 3]
	reserved2	int8
	ack_num		array[int8, 3]
# TODO: seq_num and ack_num might have different size depending on x
# TODO: options
} [packed]

dccp_packet {
	header	dccp_header
	payload	array[int8]
} [packed]

################################################################################
###################################### IGMP ####################################
################################################################################

# https://tools.ietf.org/html/rfc2236
# https://tools.ietf.org/html/rfc3376#section-4

include <uapi/linux/igmp.h>

igmp_types = IGMP_HOST_MEMBERSHIP_QUERY, IGMP_HOST_MEMBERSHIP_REPORT, IGMP_DVMRP, IGMP_PIM, IGMP_TRACE, IGMPV2_HOST_MEMBERSHIP_REPORT, IGMP_HOST_LEAVE_MESSAGE, IGMPV3_HOST_MEMBERSHIP_REPORT, IGMP_MTRACE_RESP, IGMP_MTRACE

igmp_packet {
	type	flags[igmp_types, int8]
	mrtime	int8
	csum	csum[parent, inet, int16be]
	addr	ipv4_addr
	data	array[int8]
} [packed]

# TODO: describe particular IGMP packets
# TODO: open IGMP sockets from userspace

################################################################################
###################################### MPLS ####################################
################################################################################

# https://en.wikipedia.org/wiki/Multiprotocol_Label_Switching

mpls_packet {
	labels	array[mpls_label]
	payload	mpls_payload
} [packed]

mpls_label {
	label	int32be:20
	tc	const[0, int32be:3]
	s	int32be:1
	ttl	const[0, int32be:8]
}

mpls_payload [
	generic	array[int8]
	ipv4	ipv4_packet
	ipv6	ipv6_packet
	llc	llc_packet
] [varlen]

################################################################################
###################################### TIPC ####################################
################################################################################

# http://tipc.sourceforge.net/protocol.html
# http://tipc.sourceforge.net/protocol.html#anchor50

# TODO: describe more TIPC packets, the current description is far from being complete.
# But first we need to ensure that syzkaller manages to enable TIPC receiving,
# because currently it always crashes kernel earlier.
# Also, do we need to nest TIPC packets in UDP for UDP media?

include <uapi/linux/tipc.h>
include <net/tipc/msg.h>

tipc_packet [
	payload_conn		tipc_payload_msg[tipc_payload_hdr6[TIPC_CONN_MSG]]
	payload_mcast		tipc_payload_msg[tipc_payload_hdr11[TIPC_MCAST_MSG]]
	payload_named		tipc_payload_msg[tipc_payload_hdr10[TIPC_NAMED_MSG]]
	payload_direct		tipc_payload_msg[tipc_payload_hdr8[TIPC_DIRECT_MSG]]
	name_distributor	tipc_name_distributor_msg
] [varlen]

type tipc_payload_msg[HDR] {
	hdr	tipc_payload_hdr[HDR]
	data	array[const[0, int8]]
}

type tipc_payload_hdr[HDR] {
	hdr	HDR
}

type tipc_payload_hdr6[TYP] {
# w0
	message_size		bytesize[tipc_payload_msg, int32be:17]
	y			const[0, int32be:1]
	s			int32be:1
	d			int32be:1
	n			int32be:1
	hsize			bytesize4[tipc_payload_hdr, int32be:4]
	user			flags[tipc_importance, int32be:4]
	ver			const[TIPC_VERSION, int32be:3]
# w1
	broadcast_acknowledge	int32be:16
	res			const[0, int32be:3]
	lcs			flags[tipc_scope, int32be:2]
	reroute			int32be:4
	error			flags[tipc_error, int32be:4]
	mtype			const[TYP, int32be:3]
# w2
	link_sequence		int32be:16
	link_acknowledge	int32be:16
# w3
	previous_node		int32be[0:4]
# w4
	originating_port	int32be[20000:20004]
# w5
	destination_port	int32be[20000:20004]
} [size[24]]

type tipc_payload_hdr8[TYP] {
	hdr6			tipc_payload_hdr6[TYP]
# w6
	originating_node	int32be[0:4]
# w7
	destination_node	int32be[0:4]
} [size[32]]

type tipc_payload_hdr10[TYP] {
	hdr8		tipc_payload_hdr8[TYP]
# w8
	name_type	int32be[0:4]
# w9
	name_instance	int32be[0:4]
} [size[40]]

type tipc_payload_hdr11[TYP] {
	hdr10			tipc_payload_hdr10[TYP]
# w10
	name_sequence_upper	int32be[0:4]
} [size[44]]

tipc_name_distributor_msg {
	hdr	tipc_name_distributor_hdr
	data	array[tipc_name_publication]
}

tipc_name_distributor_hdr {
# w0
	message_size		bytesize[tipc_name_distributor_msg, int32be:17]
	y			const[0, int32be:1]
	s			const[0, int32be:1]
	d			const[0, int32be:1]
	n			int32be:1
	hsize			bytesize4[parent, int32be:4]
	user			const[NAME_DISTRIBUTOR, int32be:4]
	ver			const[TIPC_VERSION, int32be:3]
# w1
	broadcast_acknowledge	int32be:16
	res			const[0, int32be:13]
	mtype			int32be:3[0:1]
# w2
	link_sequence		int32be:16
	link_acknowledge	int32be:16
# w3
	previous_node		int32be[0:4]
# w4
	originating_port	int32be[20000:20004]
# w5
	destination_port	int32be[20000:20004]
# w6
	originating_node	int32be[0:4]
# w7
	destination_node	int32be[0:4]
# w8
	res1			const[0, int32be]
# w9
	res2			const[0, int32be:23]
	m			int32be:1
	item_size		const[7, int32be:8]
}

tipc_name_publication {
	type		int32be
	lower_bound	int32be
	upper_bound	int32be
	reference	int32be
	key		int32be
	node		int32be
	scope		int32be:4
	res		int32be:28
} [size[28]]

tipc_importance = TIPC_LOW_IMPORTANCE, TIPC_MEDIUM_IMPORTANCE, TIPC_HIGH_IMPORTANCE, TIPC_CRITICAL_IMPORTANCE
tipc_error = TIPC_OK, TIPC_ERR_NO_NAME, TIPC_ERR_NO_PORT, TIPC_ERR_NO_NODE, TIPC_ERR_OVERLOAD, TIPC_CONN_SHUTDOWN
tipc_scope = TIPC_CFG_SRV, TIPC_ZONE_SCOPE, TIPC_CLUSTER_SCOPE, TIPC_NODE_SCOPE

# IPsec ESP carrying an AGGFRAG payload (IP-TFS, RFC 9347).
# The SPI comes from xfrm_spi, so a packet emitted here can match a state
# created over the XFRM netlink interface in the same program. With
# ecb(cipher_null)/digest_null the payload reaches the IP-TFS mode handler
# unchanged, which is what makes the decapsulation path reachable.
esp_aggfrag_packet {
	spi		xfrm_spi
	seq		int32be
	payload		aggfrag_payload
# ESP trailer, RFC 4303. The ICV is empty with digest_null.
	pad		array[int8, 0:8]
	pad_len		len[pad, int8]
	next_hdr	const[IPPROTO_AGGFRAG, int8]
} [packed]

aggfrag_payload [
	basic	aggfrag_basic
	cc	aggfrag_cc
] [varlen]

# struct ip_iptfs_hdr. A non-zero block_offset makes the leading bytes a
# continuation of the previous packet and drives the reassembly path. An
# offset that reaches past the end of the payload makes the whole payload a
# continuation, so the field is left unbounded.
aggfrag_basic {
	subtype		const[0, int8]
	flags		const[0, int8]
	block_offset	int16be
	blocks		array[aggfrag_inner]
} [packed]

# struct ip_iptfs_cc_hdr, the congestion control subtype.
aggfrag_cc {
	subtype		const[1, int8]
	flags		const[0, int8]
	block_offset	int16be
	loss_rate	int32be
	rtt		int64be
	tval		int32be
	techo		int32be
	blocks		array[aggfrag_inner]
} [packed]

# Inner packets are aggregated whole, their length is read from their own IP
# header. A trailing runt exercises the saved partial packet path.
aggfrag_inner [
	ipv4	ipv4_packet
	ipv6	ipv6_packet
	runt	array[int8, 1:64]
] [varlen]
