Documentation ¶
Overview ¶
Package packetman implements low-level manipulation of TCP packets, enabling a variety of strategies to evade network censorship.
This implementation is entirely based on and is a subset of Geneva:
Come as You Are: Helping Unmodified Clients Bypass Censorship with Server-side Evasion Kevin Bock, George Hughey, Louis-Henri Merino, Tania Arya, Daniel Liscinsky, Regina Pogosian, Dave Levin ACM SIGCOMM 2020 Geneva: Evolving Censorship Evasion Strategies Kevin Bock, George Hughey, Xiao Qiang, Dave Levin ACM CCS 2019 (Conference on Computer and Communications Security) https://github.com/Kkevsterrr/geneva
This package implements the equivilent of the Geneva "engine", which can execute packet manipulation strategies. It does not implement the genetic algorithm component.
Other notable differences:
- We intercept, parse, and transform only server-side outbound SYN-ACK packets. Geneva supports client-side packet manipulation with a more diverse set of trigger packets, but in practise we cannot execute most low-level packet operations on client platforms such as Android and iOS.
- For expediancy, we use a simplified strategy syntax (called transformation specs, to avoid confusion with the more general original). As we do not evolve strategies, we do not use a tree representation and some randomization tranformations are simplified.
At this time, full functionality is limited to the Linux platform.
Security: external parties can induce the server to emit a SYN-ACK, invoking the packet manipulation logic. External parties cannot set the transformation specs, and, as the input is the server-side generated SYN-ACK packet, cannot influence the packet manipulation with any external input parameters.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsSupported ¶
func IsSupported() bool
Types ¶
type Config ¶
type Config struct { // Logger is used for logging events and metrics. Logger common.Logger // ProtocolPorts specifies the set of TCP ports to which SYN-ACK packet // interception and manipulation is to be applied. To accommodate hosts with // multiple IP addresses, packet interception is applied to all interfaces. ProtocolPorts []int // On Linux, which uses NFQUEUE and raw sockets, QueueNumber is the NFQUEUE // queue-num parameter to be used. QueueNumber int // On Linux, which uses NFQUEUE and raw sockets, SocketMark is the SO_MARK // value to be used. When 0, a default value is used. SocketMark int // Specs is the list of packet transformation Spec value that are to be // available for packet manipulation. Spec names must be unique. Specs []*Spec // SelectSpecName is a callback invoked for each intercepted SYN-ACK packet. // SelectSpecName must return a name of a Spec, in Specs, to apply that // transformation spec, or "" to send the SYN-ACK packet unmodified. // The second return value is arbitrary extra data that is associated // with the packet's connection; see GetAppliedSpecName. // // The inputs protocolPort and clientIP allow the callback to select a Spec // based on the protocol running at the intercepted packet's port and/or // client GeoIP. SelectSpecName func(protocolPort int, clientIP net.IP) (string, interface{}) // SudoNetworkConfigCommands specifies whether to use "sudo" when executing // network configuration commands. See comment for same parameter in // psiphon/common/tun. SudoNetworkConfigCommands bool // AllowNoIPv6NetworkConfiguration indicates that failures while configuring // tun interfaces and routing for IPv6 are to be logged as warnings only. See // comment for same parameter in psiphon/common/tun. AllowNoIPv6NetworkConfiguration bool }
Config specifies a packet manipulation configuration.
type Manipulator ¶
type Manipulator struct {
// contains filtered or unexported fields
}
Manipulator is a SYN-ACK packet manipulator.
NFQUEUE/Netlink is used to intercept SYN-ACK packets, on all local interfaces, with source port equal to one of the ProtocolPorts specified in Config. For each intercepted SYN-ACK packet, the SelectSpecName callback in Config is invoked; the callback determines which packet transformation spec to apply, based on, for example, client GeoIP, protocol, or other considerations.
Protocol network listeners use GetAppliedSpecName to determine which transformation spec was applied to a given accepted connection.
When a manipulations are to be applied to a SYN-ACK packet, NFQUEUE is instructed to drop the packet and one or more new packets, created by applying transformations to the original SYN-ACK packet, are injected via raw sockets. Raw sockets are used as NFQUEUE supports only replacing the original packet with one alternative packet.
To avoid an intercept loop, injected packets are marked (SO_MARK) and the filter for NFQUEUE excludes packets with this mark.
To avoid breaking TCP in unexpected cases, Manipulator fails open -- allowing the original packet to proceed -- when packet parsing fails. For the same reason, the queue-bypass NFQUEUE option is set.
As an iptables filter ensures only SYN-ACK packets are sent to the NFQUEUEs, the overhead of packet interception, parsing, and injection is incurred no more than once per TCP connection.
NFQUEUE with queue-bypass requires Linux kernel 2.6.39; 3.16 or later is validated and recommended.
Due to use of NFQUEUE, larger than max socket buffer sizes, and raw sockets, Manipulator requires CAP_NET_ADMIN and CAP_NET_RAW.
func NewManipulator ¶
func NewManipulator(config *Config) (*Manipulator, error)
NewManipulator creates a new Manipulator.
func (*Manipulator) GetAppliedSpecName ¶
func (m *Manipulator) GetAppliedSpecName( localAddr, remoteAddr *net.TCPAddr) (string, interface{}, error)
GetAppliedSpecName returns the packet manipulation spec name applied to the TCP connection, represented by its local and remote address components, that was ultimately accepted by a network listener. The second return value is the arbitrary extra data returned by GetSpecName.
This allows SelectSpecName, the spec selector, to be non-deterministic while also allowing for accurate packet manipulation metrics to be associated with each TCP connection.
For a given connection, GetAppliedSpecName must be called before a TTL clears the stored value. Calling GetAppliedSpecName immediately clears the stored value for the given connection.
To obtain the correct result GetAppliedSpecName must be called with a RemoteAddr which reflects the true immediate network peer address. In particular, for proxied net.Conns which present a synthetic RemoteAddr with the original address of a proxied client (e.g., armon/go-proxyproto, or psiphon/server.meekConn) the true peer RemoteAddr must instead be provided.
func (*Manipulator) SetSpecs ¶
func (m *Manipulator) SetSpecs(specs []*Spec) error
SetSpecs installs a new set of packet transformation Spec values, replacing the initial specs from Config.Specs, or any previous SetSpecs call. When SetSpecs returns an error, the previous set of specs is retained.
func (*Manipulator) Start ¶
func (m *Manipulator) Start() (retErr error)
Start initializes NFQUEUEs and raw sockets for packet manipulation. Start returns when initialization is complete; once it returns, the caller may assume that any SYN-ACK packets on configured ports will be intercepted. In the case of initialization failure, Start will undo any partial initialization. When Start succeeds, the caller must call Stop to free resources and restore networking state.
func (*Manipulator) Stop ¶
func (m *Manipulator) Stop()
Stop halts packet manipulation, frees resources, and restores networking state.
type Spec ¶
Spec specifies a set of transformations to be applied to an intercepted SYN-ACK packet to produce zero or more replacement packets to be sent in its place.
Each element in PacketSpecs specifies a new outgoing packet. Each element in a packet specification specifies an individual transformation to be applied, in turn, to a copy of the intercepted SYN-ACK packet, producing the outgoing packet.
Syntax of individual tranformations:
"TCP-flags random|<flags>" flags: FSRPAUECN
"TCP-<field> random|<base64>" field: srcport, dstport, seq, ack, dataoffset, window, checksum, urgent
"TCP-option-<option> random|omit|<base64>" option: eol, nop, mss, windowscale, sackpermitted, sack, timestamps, altchecksum, altchecksumdata, md5header, usertimeout
"TCP-payload random|<base64>"
For example, this Geneva strategy:
[TCP:flags:SA]-duplicate(tamper{TCP:flags:replace:R},tamper{TCP:flags:replace:S})-| \/
is represented as follows (in JSON encoding):
[["TCP-flags R"], ["TCP-flags S"]]
Field and option values must be the expected length (see implementation).
A Spec may produce invalid packets. For example, the total options length can exceed 40 bytes and the DataOffset field may overflow.