Funkensturm: delaying proxy example
Another application for Funkensturm is: delaying packets.
Here we only delay packets with the recursion desired bit (RD)
set, but it can be easily be changed to check for other
properties of a packet, see godoc dns
for all elements of
DNS packets.
The configuration is similar as described here.
Matching pkts with RD bit set⌗
The matching function becomes:
// the only matching we do is on the RD bit
// for incoming packets.
func match(m *dns.Msg, d int) (*dns.Msg, bool) {
// Matching criteria
var ok bool
switch d {
case IN:
// only delay pkts with RD bit
ok = m.MsgHdr.RecursionDesired == true
case OUT:
// nothing
}
// Packet Mangling
switch d {
case IN:
// nothing
case OUT:
// nothing
}
return m, ok
}
Action function⌗
First a delay helper function. As shown here it returns true
if
the delay time isn’t reached, and false
if a something should be
delayed.
const NSECDELAY = 1 * 1e9 // 1 second, meaning 1 qps (smaller means higher qps)
var previous int64 // previous tick
// returns false if we hit the limit set by NSECDELAY
func checkDelay() (ti int64, limitok bool) {
current := time.Nanoseconds()
tdiff := (current - previous)
if tdiff < NSECDELAY {
// too often
return previous, false
}
return current, true
}
In the action function we check the delay with checkDelay
, but
only if the match function returned true.
If checkDelay
returns false, all is OK and we return the packet.
When it returns false we’ve reached the threshold, so we sleep for
some time and return a nil
packet.
func delay(m *dns.Msg, ok bool) *dns.Msg {
var ok1 bool
switch ok {
case true:
previous, ok1 = checkDelay()
if !ok1 {
fmt.Fprintf(os.Stderr, "Info: Dropping: too often")
time.Sleep(NSECDELAY)
return nil
} else {
fmt.Fprintf(os.Stderr, "Info: Dropping: too often")
qr[0] <- resolver.Msg{m, nil, nil}
in := <-qr[0]
return in.Dns
}
case false:
qr[0] <- resolver.Msg{m, nil, nil}
in := <-qr[0]
return in.Dns
}
return nil
}
Preparing the funkensturm struct⌗
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
f.Setup = func() bool { previous = time.Nanoseconds(); return true }
f.Matches = make([]Match, 1)
f.Matches[0].Op = AND
f.Matches[0].Func = match
f.Actions = make([]Action, 1)
f.Actions[0].Func = delay
return f
}
Full source of config_delay.go
Trying it!⌗
The following queries are performed in quick succession:
% dig +short +norec -p 8053 @127.0.0.1 www.example.com
192.0.32.10
Which works OK and quick, unlike in the following example, where we fire off queries with the RD bit:
% dig +short -p 8053 @127.0.0.1 www.example.com
<quick reply>
192.0.32.10
% dig +short -p 8053 @127.0.0.1 www.example.com
<quick reply>
192.0.32.10
% dig +short -p 8053 @127.0.0.1 www.example.com
<long wait>
<long wait>
192.0.32.10
In the terminal where Funkensturm runs, we see:
Info: Ok: let it through
Info: Ok: let it through
Info: Dropping: too often
Info: Ok: let it through
It works!