|
5.Logging
5.1.Logging Issues
The virtues of logging are obvious. The ability to go back and see
what what connexions had been dropped, what addresses they came from, where they were going, whether they were composed of many fragmented
packets (indicative of many DoS attacks), and so on gives you a significant edge in both knowing where connexions are being made, by whom,
and when. Especially in the case of tracking down crackers and the like, firewall logs may give one the all-important edge.
Logging also has a down-side. If you're not careful, you can both lose yourself in the abundant data (for lack of proper log analyzing
strategies) and lose your HD space to growing log files. DoS attacks that fill up HDs are one of the oldest around, and still just as dangerous to
the imprudent administrator. Although, they more often strike the poorly configure email server, they are just as much a threat to any system
keeping extensive log data. It is important that you have sufficient HD space and rotate logs prudently, so they do not grow indefinitely.
On the other hand, what many newcomers experience that once they enable ipfirewall(4) logging their terminal is overwhelmed with messages
concerning packet activity. This is the result of any combination of:
- logging too many often-matched rules
- not disabling logging to console & root terminals (bad idea!) - not controlling logging with the IPFIREWALL_VERBOSE_LIMIT
5.2.System Logging Configuration
5.2.1.Kernel Options
To Enable ipfirewall(4) logging in FreeBSD, one has to include at
least the following option in the kernel (don't forget to reboot after you build the new kernel):
optionsIPFIREWALL_VERBOSE
In addition, one may wish to enable the following option:
optionsIPFIREWALL_VERBOSE_LIMIT=#
We have already mentioned this option early on when reviewing
firewall activation. This kernel option limits the number of consecutive messages sent to the system logger, syslogd(8), concerning the activation
of a given rule. When this option is enabled in the kernel, the number of consecutive messages concerning a particular connexion are capped to the
number specified. For instance, consider the following:
optionsIPFIREWALL_VERBOSE_LIMIT=10
With this, only 10 consecutive messages concerning a particular
connexion would be logged to syslogd(8), with the remainder being subsumed under a general statement like:
Jan 29 03:26:55 myserver last message repeated 45 times
These messages are generally logged to /var/log/messages in addition to the console. If one wishes to modify this behaviour, one will
have to edit the /etc/syslog.conf. syslog.conf(5) offers considerable flixibility in how syslogd(8) will deal with system messages. The limit
which one defines for IPFIREWALL_VERBOSE_LIMIT is up to the administrator, but values above 10 or so on a busy server may still sorely populate the
console. On the other hand, if one wishes to disable kernel messages to the console altogether and log everything to a separate file (not the
default /var/log/messages) this can be done also. Indeed, in this configuration, one need not specify IPFIREWALL_VERBOSE_LIMIT at all if one
is certain that HD space is sufficient and log rotation will keep things tidy.
5.2.2. Configuring syslog(8) for Logging
One can setup syslogd(8) to log ipfirewall(4) messages to a separate file in three relatively simple steps:
1)Create your log file, and alternative, log directory. For instance, if you wish to have all of your ipfirewall(4) logs in
/var/log/ipfw/ , create the directory, and then inside, create the log file. You may call it ipfw.log, for instance. You can easily do this with the touch(1) command:
(root@nu)~># mkdir /var/log/ipfw && touch /var/log/ipfw/ipfw.log (root@nu)~>#
Make sure the directory and file are not world-readable so other users can not poke around.
2)Configure syslog.conf(5) to send ipfirewall(4) messages to
/var/log/ipfw/ipfw.log. In its most basic configuration, this can be done easily by added the following two lines to the bottom of syslog.conf(5).
!ipfw
*.*/var/log/ipfw/ipfw.log
Make sure that you use tabs and not spaces in this file. Although tabs are not required in FreeBSD for syslog.conf, it is good practice in
case you work with other UNIX systems, which may not accept spaces in their syslog.conf(5). So, eventhough you can ignore the scary message at
the top of /etc/syslog.conf, it is wiser to obide by it for the sake of maintaining a safe habit.
One should note that the "last messages repeated #" messages will be logged to this file and also any other file that is specified in
syslog.conf(5) to log *.err, kern.debug, *.notice messages, and so on. In addition, the console will always receive these messages as defined by:
*.err;kern.debug;auth.notice;mail.crit         /dev/console Â
Remember, the the console is ttyv0 (ALT + F1). Messaging to virtual and pseudo terminals will behave differently. Virtual and psudeo
terminals are controlled by the following lines in the default syslog.conf(5):
*.err                                         Â
root
*.notice;news.err                              root
*.alert                                       Â
root
*.emerg                                       Â
*
Both lines that contain *.err and *.notice will log kernel messages concerning rule matches to the terminal on which the user
specified to the right is logged in. Notice that this user is root; the best way to avoid annoying messages is to not log in as root constantly.
Logging in as root and su'ing will not save you from the messages, either. It is strongly recommended that these lines not be commented out - having
such messages logged to any terminal in which root is logged in is very useful as a quick notification of something possibly wrong happening.
Indeed, you may wish to log these or other messages to other UIDs that you use often. Instead setup your rules appropriately so only important rule
matches are logged and log in with your personal account, not root. Keep a spare virtual terminal logged in as root and check it occasionally.
To sum up, when dealing with syslog.conf(5) configuration and terminal notification:
- Add the two aforementioned lines in syslog.conf(5) to log rule
matching messages to a separate file for easy examination at a later time. - Do not worry about root terminal messages, instead do not do
your normal activity as root. This is a good all-around piece of security advice. Instead, keep a spare terminal logged in as root and check it occasionally.
3) Send a Hangup signal to the syslogd(8) process. The easiest way is with the following command:
       (root@nu)~># killall -HUP syslogd        (root@nu)~>#
5.2.3.Configuring newsyslog(8) for Log Rotation
Now that ipfirewall(4) logging is enabled, one should consider
configuring newsyslog.conf(5) so that newsyslog(8) rotates your ipfirewall(4) logs; or, alternatively, use some other log rotation
mechanism. For a full explanation of all possibler configuration options one should consult the newsyslog.conf(5) man page, however, the following
entry should suffice for most occasions (in this example, we are using the example log file name as was presented earlier):
/var/log/ipfw/ipfw.log60010*$W0D2Z
This entry can be added to the bottom of newsyslog.conf(5). The first part is self-explanatory. The second comprises the permission bits
for the rotated file The third the number of log files to keep rotating back until the oldest is deleted. The fourth is a wildcard instead of a
specific size which to use as a signal to rotate the files. The fifth part is the time at which to rotate the logs, and the sixth is a special
option. As one may have already surmised, log rotation can be done by one of two criteria: size, and time/date. We can specify to rotate a certain
log based on when it reaches a particular size, or instead, based on what time/date it is. In the above entry, we used the time/date criterion. We
specified to rotate the log at 2 AM every sunday morning, and then to gzip(1) the archive (the Z option), only allowing for a 10 archive history
(10 weeks). If one wishes to hold their logs longer than this, one need only change the third parameter from 10 to whatever they like.
Once newsyslog.conf(5) has been configured, log rotation is all done. Because newsyslog(8) is a program run from cron(8) there is no
daemon that needs to be sent the Hangup signal as was necessary for syslogd(8). There are other ways of enabling log rotation. One can write
and run one's own log-rotation script, or use a friend's that you've been impressed with. Either way, it is important that on such potentially large
log files, there be some method of archiving and controlling their growth.
5.3.Rule Logging Configuration
Once the system is fully configured to handle the ipfirewall(4) logging, we can begin specifying which rules, when matched, should be
logged. There are two simple parameters to be used in conjunction with rule formation to enable logging for the given rule; they are:
"log" - Logs everytime the rule containing this keyword is matched by a packet. The keyword, if present, must follow the "action". Make sure
you do NOT put this in wide sweeping rules such as:
add 0500 allow log all from any to any
Unless there is some extensive filtering occuring prior to this
rule, most traffic will be matched to it and one's log files will grow very large very fast. On the other hand, it could well be safe to enable
logging on a sweeping deny rule such as:
add 65000 deny log all from any to any
This rule is much safer because it is both near the end (rule #
65000 versus 500) and in rulesets that fulfill a closed firewall policy, there are generally many rules prior to the last deny rule that allow
through important traffic, which should comprise much of the total traffic experienced, except in unusual circumstances - although it can still be
risky. Take careful consideration into which rules should be logged and which shouldn't.
 "logamount <number>" - This parameter, following the "log" parameter, specifies the maximum number of matches that a rule can
experience before logging halts. This gives the administrator some extra control over logging. If, for instance, he enables 10000 matches for a
given rule, and then reset the counter once a day, he can alleviate potentially large logs if someone tries to flood the server and is blocked
by the given rule. After 10000 matches, the logging for that rule will stop, and the flooder's attack will not swell the log file. Alternatively,
one may wish to log *everything* and not use this parameter. In FreeBSD 4.x, 3.4+, and 2.2.x this is allowed, however, in earlier versions of
FreeBSD 3.x, if this keyword is not used, a default "logamount" is set to 10.
When rules are logged, the following information will be saved:
- Date & Time - Rule number - Action - Source & Destination IP addresses - Source & Destination Port numbers - Direction flow
- Device over which this occured
For instance, a firewall log line may look like this:
Jun 12 13:55:59 mybox1 /kernel: ipfw: 65000 Deny TCP
172.16.0.1:62307 192.168.0.1:23 in via xl0
       6.   Introduction to Stateless and Stateful Filtering
       Stateful and stateless filtering are two terms often encountered
in debates between proponents of ipfilter and ipfirewall(4). Stateless filtering treats each packet going through it as an individual that has no
association with the other traffic going through the given interface. This type of filtering is easier to implement and can be used effectively to:
       - filter corrupt (fragmented) packets
       - filter particular protocol packets (icmp, udp, igmp, etc)
       - do host-based filtering (filtering according to where a packet is destined, or where it came from)
       Stateful filtering is more complex to implement. It treats traffic
not as an aggregate of individual, independent packets, but as composed of connexions. All communication via any of the protocols uses sequence
numbers to indicate in which order packets should be read on a socket(2). A stateful firewall would be aware of these sequence numbers. Connexion
oriented protocols such as TCP also have special packets which indicate connexion initation (SYN) and termination (FIN), and a stateful firewall
would also be aware of these. In short, it would:
       - know what state a connexion is in
       - be able to determine if a connexion is following valid
proceedure, or is breaking the rules, and would be able to filter the packets in these connections accordingly.
       Stateful firewalls create dynamic rules for live connexions, and
clear out these rules when the connexions time out. All of this allows for a more intelligent awareness of the higher level activity of network
traffic by the firewall. On the downside, statefull firewalls are unable to treat each packet individually, because special dynamic rules are built
to allow entire connexions through, precluding the examination of packets in those rules except in the context of whether they are behaving properly
in the overall connexion. As such, it is wise to mix and match stateful and stateless rules in any firewall configuration, so one is able to benefit from both.
Almost all of the example rules that have been given so far have been stateless. The only exceptions are the rules concerning the option
"tcpflags," "setup," and "established" which allow one to check the state
of a TCP connexion. Indeed a combination of these will be used in the first stateful ruleset examples shortly. Using these options to make
primitive stateful rulesets has been functionality that has been available in ipfirewall(4) for a long time, however, because of its very limited
stateful capabilities, ipfirewall(4) has long been regarded as a stateless firewall, with IPFilter the stateful alternative. Starting with FreeBSD
4.0, ipfirewall(4) has been enabled with more extensive stateful functionality, with more promised to come.
6.1.Basic Stateful Configuration
For our first example, we will use the older, more basic, stateful capabilities of ipfirewall(4). Many people, following after the example in
~rc.firewall, where all of the pre-defined rulesets use this most basic of stateful functionality, make heavy use of the "setup" and "established"
keywords for controlling TCP connexions. Indeed, this can only be used to control TCP connexions in a stateful manner, showing its limiting nature
on at least one count. In our example, we will create a simple stateful firewall rulest that only allows ssh connexions through:
add 1000 allow tcp from any to any established add 2000 allow tcp from any to any 22 in setup
Presuming a closed firewall policy (firewall_type is NOT "OPEN" and kernel does NOT have IPFIREWALL_DEFAULT_TO_ACCEPT set) the above two
lines will first allow all established TCP connexions to pass through. Specifically, any packets part of an established TCP connexion will match
rule 1000 and then ruleset searching will cease for those packets. If, on the other hand, any packet is not part of an established TCP connexion,
rule 1000 will not match it, and ruleset analysis will move to rule 2000, where, if the packet is a SYN TCP packet destined for port 22 (port for
ssh), the rule will match it and and let pass. Subsequent packets for that connexion will be passed with rule 1000. In this manner, the above rules
are stateful as they are aware of the existence of a TCP connexion at large and not just individual packets. One could have easily accomplished
the same with stateless rules; for instance:
add 1000 allow tcp from any to any out
       add 2000 allow tcp from any to any 22 in
In this example, all packets moving out from any to anywhere are
allowed to pass through the firewall, and all packets moving to port 22 of anywhere from anywhere in, are passed through. In this case, the rules are
not aware of the TCP connexions - they do not test for initiation and established TCP connexions, but instead, let all TCP packets move out, no
matter what they are, and let all TCP packets in to port 22, no matter what they are.
This is the essence of stateful behaviour in ipfirewall(4) using
the "setup" and "established" flags: pass through TCP setup requests to specified addresses:ports and then let these established connexions through.
Let us look at a more involved example that handles ssh, email, FTP, and DNS queries for network 172.16.0.0/27:
add 1000 allow tcp from any to any established add 2000 allow tcp from any to 172.16.0.0/27 21,22,25 setup add 3000 allow udp from 172.16.0.0/27 to any 53
add 3100 allow udp from any 53 to 172.16.0.0/27
In this example, setup of TCP connexions is allowed for ports 21,
22, 25 - FTP, ssh, and email respectively, when packets are destined for the 172.16.0.0 network. Afterwards, all established TCP connexiond are
allowed to pass. Rules 3000 and 3100 pass UDP packets to port 53 on other hosts and allow UDP packets from port 53 on other hosts to pass in through
the firewall. Both of these rules are stateless. Port 53 is the port on which DNS servers run. Rule 1000 must have "from any to any" because TCP
packets for established connexions must be allowed to both originate from anywhere and to arrive at anywhere.
If one was to write a similar ruleset which was completely stateless, one would have to keep most TCP ports between 1024 and 65000
open for FTP connexions. FTP is definitely a wild protocol as it randomly binds to non-reserved ports across a wide range. Allowing active FTP
through a firewall is always difficult; only stateful firewalls offer a really clean approach to it, without opening massive ranges of
ports. Most notably the shortcoming of opening the ports with a stateless firewall is that anyone can attempt to connect to any port within that
range. With our setup, only TCP connexions that had been initiated through rule 2000 can be let through with the "established" option in rule 1000,
effectively limiting random and uncontrolled connexions to the wide range of ports needed for clean active FTPÂ operation.
6.2.Advanced Stateful Configuration
As has been stated before, stateful firewall configuration with
just the "setup" and "established" options is very limiting. Aside from only allowing stateful control over TCP connexions, this stateful control
is simplistic at best. Starting with FreeBSD 4.0 the stateful capabilities of ipfirewall(4) have been greatly augmented. Now, ipfirewall(4) can be
configured for stateful handling of TCP, UDP, ICMP and other packets types with use of dynamic rules.
Dynamic rules are another new addition to ipfirewall(4) in FreeBSD 4.0. Dynamic rules, as the name suggests, are dynamically generated for
individual connexions. Each dynamic rule, after lack of use past a period of time, times out. The specific timeout period for TCP connexions can be
controlled by several(8) sysctl variables. This allows control of not only connexion initiation, but connexion termination; that is, in a manner of
speaking, a ruleset can be designed which is aware of the beginning end ending of a particular connexion, and that adjusts itself accordingly.
One option and one command are used to control this advanced stateful behaviour:
"set-state" - Any rule with this option initiates a dynamic rule
whenever it is matched by a packet.
"check-state" - This command allows the firewall search to first
check the dynamic rules. If a rule with this command is missing from all of the rules in a ruleset, then dynamic rules are checked at the first
instance of the "set-state" option. If a rule with this command is a match, then the search stops, otherwise the search continues.
Let us return to our earlier example of a ruleset designed to only allow ssh connexions through, only this time using the advanced stateful
ipfirewall(4) facilities:
add 1000 check-state add 2000 allow tcp from any to any 22 in setup keep-state
Remember, these rules presume that your firewall policy is a closed one (the default rule is to deny everything). In the above rules,
the first rule prompts ipfirewall(4) to check the dynamic rules. If the immediate packet passing through the firewall do not belong to any already
established connexions, that is, they do not match any of the dynamic rules, then the search continues to rule 2000, where if the packet is a
TCP SYN, then "keep-state" prompts the creation of a dynamic rule. Subsequent packets through that connexion will be passed through the
dynamic rule, which is checked for with rule 1000. All other packets would be rejected by the default deny rule.
Eventhough, we have already demonstrated to do this with the older stateful approach and also a stateless approach, this new approach using
the "set-state" option and "check-state" command has a distinct advantage above the other approaches. In the older stateful approach, the option
"established" matched any TCP packets from an established TCP connexion, even if they were spoofed and not from any current legitimate TCP
connexion. This new approach precludes such an occurance. Each dynamic rule is for a specific connexion between two hosts and their respective
ports. Spoofed TCP packets will be able to spoof the source and destination IP addresses, but not the correct ports (unless they're very
lucky) on which a legitimate TCP connexion is being maintained, and thus, rule 1000 with the "check-state" command would fail, after which rule 2000
would fail unless the spoofed packet was a TCP SYN packet, and the packet would fall to the final deny rule.
In summary, the criteria with which packets are tested against when trying to match a dynamic rule for them are:
- Protocol - Source IP & Port
- Destination IP & Port - Whether the rule has timed out
As noted before, the dynamic rules time out after lack of use over
a particular period of time. Depending on how a dynamic rule is used, the rule's lifetime is given a particular period. The timeout values
for the different types of rule uses can be viewed by printing the correct sysctl variables; moreover, one can modify these values. Here is the list
of sysctl variables and their default values:
(root@nu)~># sysctl -a | grep 'dyn.*lifetime' net.inet.ip.fw.dyn_ack_lifetime: 300
net.inet.ip.fw.dyn_syn_lifetime: 20 net.inet.ip.fw.dyn_fin_lifetime: 20 net.inet.ip.fw.dyn_rst_lifetime: 5 net.inet.ip.fw.dyn_short_lifetime: 5 (root@nu)~>#
The first sysctl variable indicates that the default lifetime value to which a dynamic rule used by a TCP ACK packet is immediately set
to upon use is 300 seconds. The second variable indicates that the lifetime value to which a dynamic rule used by a TCP SYN packet is
immediately set to upon use is 20 seconds. The third variable indicates that 20 seconds is also the value to which the lifetime of a dynamic rule
used by a TCP FIN packet is set to. Dynamic rules used both by TCP RST packets and any other packets (UDP, ICMP, etc) are set with a 5 second
lifetime, as indicated by the last two sysctl variables.
Let's use an example to clarify how all of this works in action:
1) A legitimate TCP connexion request from host 172.16.0.1 on port 1234 is sent to port 22 on a server 192.168.0.1 behind the firewall. This
connexion request consists of a TCP synchronization packet (TCP SYN).
2) Rule 1000 on the firewall has the firewall check the dynamic
rules and finds none that correspond to any sort of TCP packets coming from 172.15.0.1:1234 and destined for 192.168.0.1:22.
3) Rule 2000 is checked is a match. The "keep-state" has a dynamic rule created for a TCP connexion from 172.16.0.1:1234 to 192.168.0.1:22
with the lifetime of 20 seconds (default for TCP SYN packets).
4) Within a second a TCP ACK packet is sent to 192.168.0.1:22 in
responce to the TCP ACK sent from the server to the client confirming the TCP connexion request.
5) As the packet encounters the firewall, rule 1000 once again has
the dynamic rules checked. This time a dynamic rule still within its lifetime exists which matches the protocol source IP:port and destination
IP:port, so it is let through the dynamic rule, and the packet passes through the firewall safely.
6) a spoofed TCP ACK packet from an attacker enters the
firewall which under normal circumstances could knock out the networking capability of certain unpatched Windows machines behind the firewall.
7) Rule 1000 checks the dynamic rules and finds that the spoofed packet has a destination IP:port that is an IP:port of an existing dynamic
rule, its return IP:port does not match that of any rule (because it was randomly generated by the attacker). So, rule 1000 fails and checking moves to rule 2000.
8) Rule 2000 finds the packet not to be a TCP SYN, so the check-state option is not run as the rule is not matched.
9) Consequently, the packet falls to the default deny rule and is lost.
Were this had been a stateful ruleset by the older method, the
spoofed TCP packet would've been accepted with the first rule containing the "established" option because in our original example the rule accepted
all TCP packets destined for a particular network behind the firewall that had at least the ACK flag set, and the spoofed packet indeed filfulled
these criteria. This is a first, but powerful reason for why the advanced stateful operations of ipfirewall(4) are extremely useful, and what
advantages IPFilter has in this regard also.
In our above illustration, it would have gone differently had the
spoofed packet been a TCP SYN packet, but before we describe why, let's examine a popular use of spoofed TCP SYN packets in computer
attacks, also known as SYN floods.
Spoofed TCP SYN packets are often used in network attacks. The
most common of such attack consists of sending a flood of TCP SYN packets (SYN floods) to a host so that the entire connexion queue in the prey's
kernel is saturated with open connexion attempts, thus denying the activation of new legitimate connexions. Eventhough, the FreeBSD TCP/IP
stack is designed to randomly drop TCP connexion attempts from its queue past a particular threshold of maximum tolerated TCP connexion attempts,
this type of attack can be devastating. If the TCP SYN packets come fast enough they will initiate fake connexions faster than they can be dropped
low enough to free sufficient room for all legitimate connexions. The primary variables are speed of the attack and speed of the box in its
ability to process packets moving through its TCP/IP stack. Fortunately, FreeBSD's TCP/IP stack is faster than that of Linux and many other
systesm, however, it's not always fast enough.
Just as in our previous illustration of spoofed TCP ACK packet
handling with the advanced stateful functionality, randomly spoofed TCP packets of any sort, SYN or ACK, would be unsuccessful because each new
packet with a random source IP:port would be unable to find a dynamic rule to pass through. However, if the original TCP SYN and all subsequently
spoofed TCP packets maintain the same source IP:port, then the stream could open and maintain a TCP connexion based on invalid source:port
information. Also, one must be very careful about too many dynamic rules being opened by spoofed TCP SYN packets. Although, the TCP/IP stack
wouldn't be overwhelmed by too many connexion attempts, the firewall dynamic rule queue could be saturated. One way to try to avoid this is to
shorten the lifetime of dynamic rules started by TCP SYN packets with the appropriate sysctl mentioned earlier and extending the maximum number of
dynamic rules, which is done via another sysctl:
net.inet.ip.fw.dyn_max: 1000 (default)
To get a quick count of how many dynamic rules exist, one
should print the value of the sysctl variable:
net.inet.ip.fw.dyn_count
One sure way to avoid the possibility of spoofed packets from
using up all of your dynamic rules is to disallow external hosts from initiating TCP connexions. This can be done easily with the following
rules, presuming that 192.168.0.0/27 is the net behind the firewall:
       add 1000 check-state
       add 2000 allow tcp from 192.168.0.0/27 to any out setup \ keep-state
When TCP SYN packets reach rule 2000 they fail to match and are dropped by the default deny because only TCP SYN packets moving out and
with the source address from the protected net, 192.168.0.0/27 will match the rule. Incidentally, this is the same type of protection that NAT, and
transparent proxies in general, affords for internal hosts.
Until now most of the discussion has revolved around stateful
handling of TCP connexions. However, using the advanced stateful functionality one can, as has been stated, control other packet types in a
stateful manner. For instance, during our discussion of the option "icmptypes" we presented an example of how one could use them to allow
internal hosts to ping external hosts, and deny it the other way around. This can be easily done with the "check-state" command and "set-state"
option as well:
add 1000 check-state add 2000 allow icmp from any to any out icmptypes 8 keep-state
This works simply by creating a dynamic rule for every ICMP echo request sent out. When the reply comes, it uses the dynamic rule and is
passed through. However, if someone tries to send in an ICMP echo, it is denied, unless they happen to send it during the moment our dynamic rule
is alove. ICMPs use the net.inet.ip.fw.dyn_short_lifetime sysctl variable which is by default set to 5 seconds. If the ICMP echo reply takes long
than 5 seconds to reply, the dynamic rule's life will timeout and it won't be able to pass through. If long ping replies are suspecting for being a
possibility, then that sysctl's value should be raised. Most network lag is under a second, however.
Incidentally, rule 2000 could also be written with the source address being a network address if there were more than one network behind
the firewall and one wanted to specify which network these rules should apply to. Likewise, one could limit the rule to a single host address. We
used this format because it was closest to what our first ping controlling rules used.
|
|
|