Linux Client Shim Interface

Goal

From server shim documentation:

The SHIM's goal is to provide access to all outgoing IP traffic on
a given host to an application that requires such access. Access to all packets
should be given to an application using the SHIM's services. The packets should
be complete (including IP and transport layer headers), and fully processed by
the networking stack (in the case of outgoing packets), that is routing,
fragmentation should already have been operated on the packets.

The SHIM also provides a filtering mechanism, so that not all packets are
captured and sent to the resquesting application, but only those packets
deemed interesting for that application. This is done using filtering rules,
which can be based on the following criteria:

- Source IP Address / Mask
- Destination IP Address / Mask
- Source Port        (supported transport protocols are TCP and UDP)
- Destination Port   (supported transport protocols are TCP and UDP)
- Transport Protocol
- User ID
- Process ID
- Session ID

Also, the SHIM provides a mechanism to send application-created or
application-processed packets back "up" to the stack, as if these packets had
been coming directly from the network all along. It does not provide a
mechanism to send packets "down" in the stack and on to the network, as this
functionality can be attained by using regular sockets.

Captured packets are effectively removed from the networking stack, and will
never reach their originally intended destination unless the application
resends the packet (or a modified version of it) in the stack using sockets or
the SHIM.

   For client use, the scope of the shim's functionality has been expanded. In addition to providing the filtering criteria described  above, two types of filtering rules are now supported. The second type of rule is not used to selectively transfer IP datagrams to user processes, but to selectively redirect outgoing socket connections to alternate IP addresses or ports. These rules expand the functionality of the shim to expose an interface which permits similar manipulation of outbound socket connections to those achieved by zklsp.dll on windows.


API

From server shim documentation:

The SHIM is opened as a device, the application opening it thus getting a file
descriptor to access its services. This file descriptor is used three different
ways:

- ioctl's are used to add/remove/modify filtering rules.
- Reading the file descriptor will give the application the contents of the
  filtered packets.
- Writing to the file descriptor will send the given buffers back "up" in the
  networking stack.

Note that different applications (or a single application for that matter) with
the same user ID can get different file descriptors to the shim, but will only
have a single common set of filtering rules. This means all calls to modify the
filtering rules by an application running under a given uid will modify the
filtering rules on all shim file descriptors running under the same uid.

    For client purposes the above remains true, but the ioctl interface used  to add / remove rules has been substantially modified. In order to support some of the more complicated logic required by the freedom client, especially on a multiuser platform like Linux, the format of shim rules has been modified to allow 'special case' rules to change the behaviour of other more general rules. In addition, a number of changes have been made to support redirection of sockets in addition to filtering of packets.

Client Shim IOCTLS

   In addition to modified server shim ioctls, the client shim supports some entirely new ioctls:

#define ZKIOC_ADD_RULE _IOW (ZKSHIM_IO CTL_ID, 0x01, ZkShimRule)

   When called on an open file descriptor for the /dev/zkshim device, this ioctl inserts a new rule into the shim kernel module's rule chian. The single argument is a structure describing the rule to insert.

#define ZKIOC_DEL_RULE _IOW (ZKSHIM_IOCTL_ID, 0x02, ZkShimRule)

   When called on an open file descriptor for the /dev/zkshim device, this ioctl removes a matching rule from the shim kernel module's rule chian. The single argument is a structure describing the rule to remove.

#ifdef CLIENT_SHIM // client shim specific
#  define ZKIOC_CLEAR_RULES _IO (ZKSHIM_IOCTL_ID, 0x03)

   When called on an open file descriptor for the /dev/zkshim device this ioctl removes all rules from the shim kernel module. (intended to assist changing large numbers of rules when, for example, a user begins or stops using a nym)

#if 0 // partially implemented
#  define ZKIOC_SIGPROMPT_ENABLE _IOW (ZKSHIM_IOCTL_ID, 0x04, int)
#  define ZKIOC_SIGPROMPT_DISABLE _IO (ZKSHIM_IOCTL_ID, 0x05)
#  define ZKIOC_SIGPROMPT_REPLY _IOW (ZKSHIM_IOCTL_ID, 0x06, int)

   When called on an open file descriptor for the /dev/zkshim device, these rules enable, disable, and reply to POSIX.1b realtime signal notifications generated by ZSA_PROMPT rules (see below). This is not fully implemented yet.

Client Shim Rule Format

typedef struct ZkShimRule {
   uint8_t        zru_ip_tos;
   uint8_t        zru_ip_p;
   uint32_t    zru_ip_src, zru_ip_src_mask;
   uint32_t    zru_ip_dst, zru_ip_dst_mask;
   uint16_t    zru_sport;
   uint16_t    zru_dport;
   uid_t        zru_uid;
   pid_t        zru_pid;
   pid_t        zru_sid;
#ifdef CLIENT_SHIM
   // Client extensions.
   uint8_t        zru_type;
   uint8_t        zru_action;
   uint32_t    zru_ip_arg;
   uint16_t    zru_port_arg;
#endif // CLIENT_SHIM
} ZkShimRule;

   Shim rules can be dynamicly inserted and deleted from dev/zkshim devices and the sequence in which inserts and deletions occur is important since rules are evaluated in LIFO (verify this) order. Each rule structure consists of a series of members which are used to determine which packets / socket calls a rule will match, and, in the client implementation, a series of additional members which determine what actions the shim kernel module will take when a match is found. As a client extension to the server shim, certain actions have inversions which, combined with the sequence in which rules are evaluated, allow specific exceptions to occlude more general rules.

   uint8_t        zru_ip_tos;
       IP type-of-service. 0 is wildcard.

   uint8_t        zru_ip_p;
       IP protocol number. no wildcard (verify)

   uint32_t    zru_ip_src, zru_ip_src_mask;
       IP source address and netmask. 0.0.0.0/0.0.0.0 is wildcard.

   uint32_t    zru_ip_dst, zru_ip_dst_mask;
       IP destination address and netmask. 0.0.0.0/0.0.0.0 is wildcard.

   uint16_t    zru_sport;
       IP source port. 0 is wildcard.

   uint16_t    zru_dport;
       IP destination port. 0 is wildcard.

   uid_t        zru_uid;
   pid_t        zru_pid;
   pid_t        zru_sid;
       Socket User ID, Process ID, and Session ID for owner of source socket. 0 is wildcard, and the only way to match non-local sockets.

   uint8_t        zru_type;
       Rule type. This may be one of the following constants:

           ZRU_SOCKET
               Rule applies to outgoing socket connections.

           ZRU_FILTER
               Rule applies to packets being forwarded or output by the kernel IP stack (note: NOT input).

   uint8_t        zru_action;
       Rule action.  This value of this member determines what action the shim kernel module will take when a packet matches this rule, it may be set to one of the following constants:

       ZSA_ALLOW
   Allow default behaviour to take place. For ZRU_SOCKET rules, this action causes the socket() call to be directed to the rest of the IP stack for processing. For ZRU_FILTER rules, this action causes the packet to be forwarded to a device driver for output. This action serves only to provide support for special case exceptions to  other rules.

      ZSA_DENY
   Deny furthur processing. For ZRU_SOCKET rules, this action causes the socket() call to fail and set errno to ECONREFUSED. For ZRU_FILTER rules, this action causes a silent (firewall) discard, dropping the packet, and providing no notification.

       ZSA_REDIRECT
   Redirect processing to user process. For ZRU_SOCKET rules, this action causes the socket to connect to the IP and port specified in zru_ip_arg and zru_port_arg. For ZRU_FILTER rules, this action causes the filtered packet to be appended tothe /dev/zkshim devices read queue, where it can be read by the process that ioctl()ed the rule into the kernel.

       ZSA_REDIRECT_TXIP
   Redirect processing to user process and transmit original destination IP. For ZRU_SOCKET rules, this action causes the same behaviour as above, but transmits the original destination IP as the first four bytes of data sent on the connection. For ZRU_FILTER rules this action is invalid (and should cause an EINVAL result from a call to ioctl which tries to insert a filtering rule that uses it).

       ZSA_PROMPT
    Not fully implemented. Prompt user process for action. For ZRU_SOCKET  rules, this action requests notification via a POSIX.1b signal whenever  a match is found. Once notified the user process which inserted the rule  may ioctl() the /dev/zkshim device using ZKIOC_SIGPROMPT_REPLY to inform  the kernel how to proceed. This stuff is about 50% done, but is contingent  on the email story. The requirement that makes this a necessarry feature  is that user-specific notification currently has to be sent to users when  they try to connect to their POP box without freedom running. This (somewhat  convoluted) mechansim allows a user-land process to tell the kernel what  to do in this case. Coupled with a little monitor application which runs whenever a user is logged in, this could achieve parity with the way things work on windows.

   uint32_t    zru_ip_arg;
   uint16_t    zru_port_arg;
       The IP address and port to use as arguments for the action specified in zru_action.


    Using this relatively simple structure for rules, the linux shim kernel  module can provide some relatively complex matching logic. For example, assuming a user 100 is running freedom as process 123 and using a nym,  the following rules would achieve logic similar to win32 for HTTP (protocol is TCP and TOS is wild):

srcIP/msk
srcPort
dstIP/msk
dstPort
uid
pid
type
action
argIP
argPort
comment
0.0.0.0/0
0
0.0.0.0/0
80
100
123
FILTER
REDIRECT


freedomize
0.0.0.0/0
0
0.0.0.0/0
80
100
123
SOCKET
ALLOW


proxy->net
0.0.0.0/0
0
0.0.0.0/0
80
100
0
SOCKET
REDIRECT_TXIP
127.0.0.1
httpproxy
local->proxy
0.0.0.0/0
0
0.0.0.0/0
httpproxy
100
123
FILTER
ALLOW


freedom user
0.0.0.0/0
0
0.0.0.0/0
httpproxy
0
0
FILTER
DENY


discard others


     Note how the bottom two rules make use of the ZSA_ALLOW action to provide  for an exception, all traffic to the httpproxy should be discarded except  packets sent by uid 100's freedom process. The two LSP style SOCKET rules  work in a similar manner, redirecting all connections to port 80 which do  not originate from freedom to the freedom local proxy. The topmost rule  would permit the trafficfilter to be choosy about which packets it wants  to recieve, in this case only packets going to port 80 on the internet,  which originated from the freedom process need to be transfered to the trafficfilter  (which could read them from an open handle on /dev/zkshim). It's also important to realize that while the source IP is never explicitly 127.0.0.1 in the example, any rule which includes a uid or pid assumes that this must be the case.

Notes