summaryrefslogtreecommitdiff
path: root/common/packet.h
blob: 35c57e158a71bdd01f2ea97537bfd5f8749fdf60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#pragma once

enum packet_type {
   PACKET_IOCTL,
   PACKET_WRITE,
};

enum ioctl_dir {
   IOCTL_NONE,
   IOCTL_IOR,
   IOCTL_IOW,
   IOCTL_IOWR,
};

enum ioctl_arg {
   IOCTL_ARG_INT,
   IOCTL_ARG_OTHER, // All bets off, can't assure portability
};

/**
 * Lackluster "network transparent" ioctl call
 * Implements enough to have remote uinput interface.
 */
struct ioctl {
   /**
    * Because the 32bit encoding of ioctl message is not stable ABI interface between CPU architectures,
    * we'll write the raw _IOC arguments on the wire and reconstruct the 32bit encoding on uinputd.
    */
   uint8_t dir; // enum ioctl_dir, we can't use bitmask directly, as it's not portable
   uint8_t type; // type of ioctl, e.g. UINPUT_IOCTL_BASE
   uint8_t nr; // command code (nr), e.g. 100 of UINPUT_IOCTL_BASE which means UI_SET_EVBIT

   /**
    * Ioctl ABI also includes argument size part, which is mainly used for typechecking.
    * However since the arguments are not exact size types, but again CPU architecture dependant sizes, and even
    * compiler dependant for structs (even though they use __u32 and friends, the padding may differ), we can't
    * really do generic network abstraction.
    *
    * To avoid creating wrapper/shim over uinput/evdev (or for other linux interfaces if ever needed to be expanded), we
    * use another type enum for ioctl argument type, for structs all bets are off, they may work or not work at all.
    * (e.g. the ioctl will fail since it won't pass typecheck due to different sized of structs in between client <-> server)
    */

   uint8_t arg; // enum ioctl_arg, ^ read above

   union {
      uint32_t integer;
      uint16_t bytes; // max ioctl parameter size can be 16kB -1
   };

   // payload, for IOCTL_ARG_OTHER, read the amount of bytes indicated by 'bytes'
};

/**
 * Network transparent write call
 *
 * The call itself is portable, the underlying data may not be.
 * E.g. structs used by evdev aren't packed, and some of them contain non explicit types such as POSIX struct timeval.
 */
struct write {
   uint64_t bytes; // amount of bytes to be written

   // payload, read the amount of bytes indicated by 'bytes'
};

/**
 * Binary specification for uinputd messages.
 * All fields must be in little-endian.
 *
 * All packet operations are applied into the open uinput fd.
 */
struct packet {
   uint8_t type; // enum packet_type

   union {
      struct ioctl ioctl;
      struct write write;
   };
} __attribute__((packed)) __attribute__((scalar_storage_order("little-endian")));