Network policy reference

The complete vocabulary of PNP rules as the kernel reads them. The concepts — layers, the forest, the laws — are in Network policy; this page is the lookup table.

Registry layout #

Machine\System\Network\Rules
    CurrentReportingLevel        REG_DWORD 1..6   (optional; absent = 1)
    RawPacket\                   the wire-side layer's forest
        <rule>\
    Packet\                      the per-packet layer's forest
        <rule>\                  one tree root
            <exception>\         a subkey: narrower region, same laws
                ...
    Flow\                        the flow layer's forest
        <rule>\

A rule key's name is its attribution handle and may not contain \ or /. Layer keys other than RawPacket, Packet and Flow are ignored.

Rule values #

ValueTypeMeaning
ActionsREG_MULTI_SZThe action list, one expression per element. Absent or empty = NULL (abstain).
PriorityREG_DWORD / REG_QWORDCollation weight. Inherited by subkeys; a root defaults to 0.
EnabledREG_DWORD 0 or 10 disables the rule and its whole subtree. Default 1.
<Fact>.<Operator>see belowA condition. Any other value name is an error that refuses the generation.

The Rules key's own value #

ValueTypeMeaning
CurrentReportingLevelREG_DWORD 1..6REPORT(n) fires when n >= CurrentReportingLevel. Absent = 1 (everything fires); 6 silences all reports. Out of range refuses the generation.

Conditions #

A condition is a value named <Fact>.<Operator>. A rule matches when every condition holds; a condition over a fact the packet lacks is false (the absent-fact law).

Operators #

OperatorFamiliesValue
EqualallMembership: a scalar, or a list (REG_MULTI_SZ) meaning any of. Integers accept a-b ranges; addresses accept CIDR prefixes and (IPv4) a-b ranges.
GreaterThanintegerA single integer; strictly greater.
LessThanintegerA single integer; strictly less.
HasflagsA flag name or list; all listed bits set.
HasntflagsA flag name or list; all listed bits clear.

Integer values may be written as REG_DWORD, REG_QWORD, or as decimal strings; a list of integers is a REG_MULTI_SZ of decimal strings and ranges. Applying an operator a family does not support (GreaterThan on an address, Has on a port) refuses the generation.

Facts #

FactFamilyValuesPresent when
Directionstringin, outalways. Flow: the originator's side, for the flow's whole life
Interfacestringinterface name, e.g. eth0always. Flow, outbound: the route's device
EtherTypeintegernumber, or ipv4, ipv6, arpalways. Not a Flow fact (a flow's family is its addresses')
SrcMacMACaa:bb:cc:dd:ee:ffframe has an Ethernet header; at the IP seats outbound, the machine's own device address
DstMacMACaa:bb:cc:dd:ee:ffframe has an Ethernet header. Not a Flow fact
VlanintegerVLAN idthe frame is VLAN-tagged (device seats) or the interface is a VLAN device (IP seats)
SrcAddr, DstAddraddressexact (10.0.0.5, fd00::1), CIDR (10.0.0.0/8, fd00::/64), IPv4 range (10.0.0.1-10.0.0.99)IP packet
Protocolintegernumber, or tcp, udp, icmp, icmpv6, sctpIP packet
TtlintegerTTL / hop limitIP packet. Not a Flow fact
Dscpinteger0..63IP packet. Not a Flow fact
Fragmentinteger0 or 1IP packet (only ever 1 before defragmentation, i.e. at the RawPacket seat). Not a Flow fact
SrcPort, DstPortinteger0..65535TCP, UDP or SCTP
TcpFlagsflagsFIN, SYN, RST, PSH, ACK, URG, ECE, CWRTCP. Not a Flow fact
IcmpType, IcmpCodeintegerICMP / ICMPv6 type and codeICMP or ICMPv6
Lengthintegerpacket length in bytes at the seat (stack view)always. Not a Flow fact
FlowStatestringnew, established, related, invalid, untrackedPacket layer at its proper seat; never at the RawPacket or Flow layers
Relatedinteger0 or 1: the flow was expected by another (an ICMP error for a live flow, FTP data)Flow layer only
Time.Year, Time.Month, Time.DayOfMonth, Time.DayOfWeek, Time.Hour, Time.Minute, Time.Secondintegerwall clock, UTC; DayOfWeek is ISO (1 = Monday .. 7 = Sunday)always. In Flow, a consulted condition expires the sentence at its next flip
Start.Year, Start.Month, Start.DayOfMonth, Start.DayOfWeek, Start.Hour, Start.Minute, Start.Secondintegerthe wall clock when the flow began, UTCFlow layer only; fixed for the flow's life, never expires a sentence
Tag.<name>integerthe flow tag's valuethe flow carries the tag (Packet and Flow layers; never RawPacket)
Counter.<name>[(...)]integera counter view, see belowthe packet has the view's key facts and a cell exists

Address families are strict: an IPv4 pattern never matches an IPv6 address and vice versa, so 0.0.0.0/0 does not swallow IPv6.

A Flow fact is one that is identical for every packet of the flow. The per-packet facts marked "not a Flow fact" are legal in a Flow rule but never present there, so the condition never holds; the viewer flags it. Related and Start.* are the reverse: never present outside Flow.

Counter views #

Counter.<name> reads the stream <name> that some rule's COUNT writes. In parentheses, in any order, at most one of each:

ArgumentFormMeaning
window<n>s, <n>m, <n>h, <n>d — at most 1dSliding window; omitted = the cumulative total since the cell was created.
keySrcAddr, DstAddr, Interface, or a +-compound (SrcAddr+DstAddr)Which facts partition the count; omitted = one global cell.

Examples: Counter.dns (total, global); Counter.dns(10s) (last ten seconds, global); Counter.dns(SrcAddr) (total, per source); Counter.dns(1h, SrcAddr+DstAddr) (last hour, per source–destination pair). A view over a stream that no rule anywhere writes refuses the generation; a stream nobody reads is fine. Windows are approximated by eight buckets, so a value can be up to an eighth of a window stale.

Actions #

Actions is a list of expressions. Names are case-insensitive; whitespace is ignored. A rule's actions may contain any mix; at most one verdict results (the strictest listed).

ExpressionSpeciesMeaning
NULLabstentionNothing. A rule whose actions yield no verdict abstains.
PASSverdictThis layer approves.
DROPverdictRefuse silently.
REJECT / REJECT(Refused)verdictRefuse; look like nothing is listening — TCP RST, else ICMP port-unreachable.
REJECT(Prohibited)verdictRefuse; say policy did it — ICMP admin-prohibited for every protocol.
PROMPT(Handler[, Fallback])deferralAsk a userspace handler; on no answer apply Fallback (an action expression, nesting up to 4 deep). No handler transport exists in this release: the fallback applies immediately and the prompt is recorded.
TAG(Name, Set[, n])effectSet flow tag Name to n (default 1).
TAG(Name, Add[, n])effectAdd n (default 1) to the tag; an absent tag counts as 0 first.
TAG(Name, Clear)effectRemove the tag; it reads as absent afterwards.
COUNT(Name[, n])effectEmit n (default 1) into stream Name.
COUNT(Name, Length)effectEmit the packet's byte length — the bandwidth primitive.
REPORT(level)effectEmit a network-report audit event, level 1..5, if the level clears CurrentReportingLevel. One report per rule per evaluation, at the highest level listed.

Unknown action names, wrong argument counts, negative or non-integer operands, unminted REJECT kinds, and a TAG operation other than the three above all refuse the generation.

Where a REJECT can speak #

Everywhere, for IP traffic. Inbound, the refusal goes back to the peer. Outbound, the answer the peer would have sent is delivered to the local socket, so the program's connect fails at once with connection refused (Refused, TCP) or host unreachable (Prohibited, or any UDP refusal). Only a packet with no refusal vocabulary — ARP and other non-IP frames, a broadcast or multicast destination, a fragment — is applied as a DROP and counted as degraded; the verdict event still says REJECT and names the kind. The refusals PNP sends pass its own seats unjudged.

In the Flow layer a REJECT sentence answers every later packet of the flow the same way, so a retransmitted SYN gets its reset too. When the refused packet belongs to an established TCP connection (a flow re-judged after a policy change or at a time edge), the other end is torn down as well: the refused packet is turned into a reset and sent where it was going, so both sockets fail at once. New flows and UDP have no far end to tear down.

Layers #

RawPacketPacketFlow
Judgedat the device seats, for all traffic, per packetonce per traversal, at the richest seat, per packetonce per flow (per local endpoint), at the IP seats, on the flow's first packet
Orderfirst inbound, last outboundbetweenlast inbound (after Packet), first outbound (before Packet)
FlowStatenever (the seat stands before conntrack)yesnever (the layer is the judgment of a flow)
Related, Start.*neverneveryes
Per-packet facts (Length, TcpFlags, Fragment, Ttl, Dscp, EtherType, DstMac)yesyesnever
Tag.<name> readsnevertags RawPacket or Packet rules writeany tag
TAG writesyesyesyes
Fragmentmay be 1always 0
Effects runper packetper packetper evaluation of the flow
Verdict scopethe packetthe packetthe flow: cached as its sentence

A rule conditioned on a fact its layer never has is legal but can never match; the viewer flags it. A Packet or RawPacket rule that reads a tag a Flow rule writes is a downward read and refuses the generation.

Sentences #

The Flow layer's verdict for a flow is cached on the flow with the policy generation that judged it and an expiry. A packet of a flow whose sentence is current is not evaluated (and emits no event). A sentence is stale, and the flow re-judged on its next packet, when:

  • the policy generation has changed since the judgment, or
  • a live-time condition (Time.*) the judgment consulted — true or false — would have flipped by now. Hour, minute, second and day-of-week conditions flip exactly when their value would next change the condition's answer; day-of-month, month and year conditions re-judge daily at midnight UTC.

A re-judgment is a full evaluation: effects run again. A flow that conntrack could not give an extension (allocation failure at creation) holds no sentence and is evaluated on every packet, counted.

A loopback flow has two local endpoints and two sentences: judged as out at the outbound seat and as in at the inbound seat, and every packet of it answers to the stricter of the two.

Limits #

LimitValueWhen exceeded
Distinct tags per flow64the write is refused and counted
Keys per counter table4096idle keys are reaped; if none are idle the new key is refused and counted
Windows per (stream, key)8the generation is refused
Longest window1 daythe generation is refused
Rule nesting depth12the generation is refused
Rules per layer4096the generation is refused
Attribution path in events96 bytestruncated
Tags reported per flow in the flows dump8the rest are counted, not listed

The development baseline #

Experimental images ship a seed policy so a fresh machine works while still refusing unsolicited inbound connections. The compiled-in backstop is DROP in every layer; each line below is a visible, deletable yes.

Rules\RawPacket:

RuleConditionsActions
allPASS

Rules\Packet — passes every tracked packet through to Flow, and the untracked housekeeping a host needs:

RuleConditionsActions
trackedFlowState.Equal = new, established, relatedPASS
outbound-okDirection.Equal = outPASS
loopbackInterface.Equal = loPASS
arpEtherType.Equal = arpPASS
icmpv6-housekeepingProtocol.Equal = icmpv6, IcmpType.Equal = 130-137, 143PASS
dhcp-clientProtocol.Equal = udp, SrcPort.Equal = 67, DstPort.Equal = 68PASS

Rules\Flow — the decisions:

RuleConditionsActions
outbound-okDirection.Equal = outPASS
loopbackInterface.Equal = loPASS
dev-viewer-portsDirection.Equal = in, Protocol.Equal = tcp, DstPort.Equal = 8080, 8081PASS

Every other inbound flow meets the Flow backstop, once, on its first packet; untracked inbound packets meet the Packet backstop.

Worked examples #

An exception under a broad rule — drop all inbound except SSH from the LAN, and tell other SSH sources that policy said no:

Rules\Packet\no-inbound            Direction.Equal = in
                                   Actions = DROP
Rules\Packet\no-inbound\ssh        DstPort.Equal = 22
                                   Actions = REJECT(Prohibited)
Rules\Packet\no-inbound\ssh\lan    SrcAddr.Equal = 10.0.0.0/8
                                   Actions = PASS

A flow tag read on the reply — mark DNS queries, report on the replies of marked flows:

Rules\Packet\tag-dns        Protocol.Equal = udp, DstPort.Equal = 53
                            Actions = TAG(dnsq, Add)
Rules\Packet\tagged-reply   Protocol.Equal = udp, SrcPort.Equal = 53
                            Tag.dnsq.GreaterThan = 0
                            Actions = REPORT(4)

Byte accounting per interface — count outbound bytes and refuse a source that pushes more than 100 MB in a minute:

Rules\Packet\egress-bytes   Direction.Equal = out
                            Actions = COUNT(egress, Length)
Rules\Packet\egress-cap     Direction.Equal = out
                            Counter.egress(1m, SrcAddr).GreaterThan = 104857600
                            Actions = DROP

A connection-level allow — let the machine connect out, accept SSH in from the LAN, and refuse every other inbound connection with a reset, each decided once per flow:

Rules\Flow\outbound-ok      Direction.Equal = out
                            Actions = PASS
Rules\Flow\inbound          Direction.Equal = in
                            Actions = REJECT
Rules\Flow\inbound\ssh-lan  Protocol.Equal = tcp, DstPort.Equal = 22
                            SrcAddr.Equal = 10.0.0.0/8
                            Actions = PASS

A curfew that lets downloads finish — no new outbound connections between 22:00 and 06:00 UTC, existing ones run on:

Rules\Flow\curfew           Direction.Equal = out
                            Start.Hour.Equal = 22-23, 0-5
                            Priority = 10
                            Actions = REJECT(Prohibited), REPORT(3)

Written with Time.Hour instead of Start.Hour, the same rule cuts every running outbound connection at 22:00 — on its next packet, with an ICMP admin-prohibited — and the report says which rule did it.

Connection rate limiting — count connections, not packets, by counting in the flow layer:

Rules\Flow\conn-count       Direction.Equal = in, Protocol.Equal = tcp
                            Actions = COUNT(conns)
Rules\Flow\conn-flood       Direction.Equal = in, Protocol.Equal = tcp
                            Counter.conns(1m, SrcAddr).GreaterThan = 100
                            Actions = DROP

Edit this page