Initial import of tap-bridge
authorMatthew Mondor <mmondor@pulsar-zone.net>
Mon, 17 Dec 2007 19:56:07 +0000 (19:56 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Mon, 17 Dec 2007 19:56:07 +0000 (19:56 +0000)
mmsoftware/tap-bridge/GNUmakefile [new file with mode: 0644]
mmsoftware/tap-bridge/doc/packets.txt [new file with mode: 0644]
mmsoftware/tap-bridge/doc/tap-bridge.dia [new file with mode: 0644]
mmsoftware/tap-bridge/doc/tap-bridge.png [new file with mode: 0644]
mmsoftware/tap-bridge/modules/modules.conf [new file with mode: 0644]
mmsoftware/tap-bridge/modules/tap-logger.c [new file with mode: 0644]
mmsoftware/tap-bridge/modules/tap-passthrough.c [new file with mode: 0644]
mmsoftware/tap-bridge/tap-bridge.c [new file with mode: 0644]
mmsoftware/tap-bridge/tap-bridge.h [new file with mode: 0644]
mmsoftware/tap-bridge/tap-headers.h [new file with mode: 0644]

diff --git a/mmsoftware/tap-bridge/GNUmakefile b/mmsoftware/tap-bridge/GNUmakefile
new file mode 100644 (file)
index 0000000..4f37af9
--- /dev/null
@@ -0,0 +1,39 @@
+# $Id: GNUmakefile,v 1.1 2007/12/17 19:56:07 mmondor Exp $
+
+MMLIB_PATH := ../../mmlib
+
+MMLIBS := $(addprefix $(MMLIB_PATH)/,mmpool.o mmhash.o mmlog.o)
+LIBS := -lc
+BINOBJS := tap-bridge.o
+MODULES := $(addprefix modules/,tap-logger.so tap-passthrough.so)
+CFLAGS += -Wall
+#CFLAGS += -g -DDEBUG
+BIN := tap-bridge
+
+%.o: %.c
+       cc -c ${CFLAGS} -I. -I$(MMLIB_PATH) -o $@ $<
+
+%.so: %.c
+       cc -shared -fPIC ${CFLAGS} -I. -I$(MMLIB_PATH) -o $@ $<
+
+$(BIN): $(MMLIBS) $(BINOBJS) $(MODULES)
+       cc -rdynamic -o $@ $(MMLIBS) $(BINOBJS) $(LIBS)
+
+all: $(BIN)
+
+
+clean:
+       rm -f $(BIN) $(BINOBJS) $(MODULES) $(MMLIBS)
+
+
+install:
+       install -d -o 0 -g 0 -m 755 /usr/local/sbin
+       install -c -o 0 -g 0 -m 500 tap-bridge /usr/local/sbin
+       # XXX Automate for each module
+       install -d -o 0 -g 1 -m 750 /usr/local/lib/tap-bridge
+       install -c -o 0 -g 1 -m 550 modules/modules.conf \
+           /usr/local/lib/tap-bridge
+       install -c -o 0 -g 1 -m 550 modules/tap-logger.so \
+           /usr/local/lib/tap-bridge
+       install -c -o 0 -g 1 -m 550 modules/tap-passthrough.so \
+           /usr/local/lib/tap-bridge
diff --git a/mmsoftware/tap-bridge/doc/packets.txt b/mmsoftware/tap-bridge/doc/packets.txt
new file mode 100644 (file)
index 0000000..f0540f9
--- /dev/null
@@ -0,0 +1,121 @@
+Note that there is now a fourth 16-bit field which is used for 32-bit padding
+to 32-bit align protocol header structures.  This is not shown here as those
+packets were captured before the addition of the field.
+
+        000   00 06 29 d0 66 e3|f2 0b  a4 e9 70 0e|08 00|45|00|..).f.....p...E.
+              1                 2                  3     4  5
+        010   00 38|c4 7b|00 00|40|11| 32 dc|c0 a8 01 0c|c0 a8 .8.{..@.2.......
+              6     7     8     9  10  11    12          13
+        020   01 01|f3 a5|00 35|00 24| 90 43|e4 70 01 00 00 01 .....5.$.C.p....
+                    14    15    16     17    18
+        030   00 00 00 00 00 00 06 67  6f 6f 67 6c 65 03 63 6f .......google.co
+        040   6d 00 00 01 00 01                                m.....
+
+Minimal ethernet frame:
+1 - Destination MAC address
+2 - Origin MAC address
+3 - Ether type
+
+Minimal IP header:
+4 - Version (4) + Header length (5)
+5 - Type of service (0)
+6 - Total length (54)
+7 - Identification
+8 - Flags + Fraglent offset
+9 - Time To Live (64)
+10 - Protocol (17) (UDP)
+11 - Header Checksum
+12 - Source address (192.168.1.12)
+13 - Destination address (192.168.1.1)
+<empty/no options>
+
+Minimal UDP header:
+14 - Source port (62373)
+15 - Destination port (53)
+16 - Length (36)
+17 - Checksum (http://tools.ietf.org/html/rfc768, can be optional if 0x0000)
+
+18 - Data (36 bytes of UDP header + data)
+
+        000   00 06 29 d0 66 e3|f2 0b  a4 e9 70 0e|08 00|45|00|..).f.....p...E.
+              1                 2                  3     4  5
+        010   00 54|c4 7c|00 00|ff|01| 4d 2b|c0 a8 01 0c|40 e9 .T.|....M+....@.
+              6     7     8     9  10  11    12          13
+        020   a7 63|08|00|45 d7|71 51| 00 00|47 5f fe 2e 00 01 .c..E.qQ..G_....
+                    14 15 16    17     18    19
+        030   10 45 08 09 0a 0b 0c 0d  0e 0f 10 11 12 13 14 15 .E..............
+        040   16 17 18 19 1a 1b 1c 1d  1e 1f 20 21 22 23 24 25 ...........!"#$%
+        050   26 27 28 29 2a 2b 2c 2d  2e 2f 30 31 32 33 34 35 &'()*+,-./012345
+        060   36 37                                            67
+
+10 - ICMP (1)
+
+Minimal ICMP header:
+14 - Type
+15 - Code
+16 - Checksum
+17 - ID
+18 - Sequence
+
+19 - Padding data (implementation-dependent, IP length header is used)
+
+        000   f2 0b a4 e9 70 0e|00 b0  d0 83 dc d5|08 00|45|10 ....p.........E.
+              1                 2                  3     4  5
+        010   00 64|ce 92|00 00|40|06| 28 8b|c0 a8 01 0a|c0 a8 .d....@.(.......
+              6     7     8     9  10  11    12          13
+        020   01 0c|00 16|f0 22|df 17  3f fa|b1 70 87 8c|80|18 ....."..?..p....
+                    14    15    16           17          18|19
+        030   83 2c|00 0c|00 00|01 01  08 0a 00 01 7e 5c 00 01 .,..........~\..
+              20    21    22    23
+        040   7e 56|fa f8 ce 9a 8d fd  79 ee f3 d6 fe 06 e1 28 ~V......y......(
+                    24
+        050   6c 69 99 ef c9 83 97 31  ab b8 8a f9 2d 18 62 bc li.....1....-.b.
+        060   48 91 5b d6 04 70 77 77  96 be 74 8c 35 92 f8 a6 H.[..pww..t.5...
+        070   fe f9                                            ..
+
+10 - TCP (6)
+
+Minimal TCP header:
+14 - Source port
+15 - Destination port
+16 - Sequence number
+17 - Aknowledgement number
+18 - Data offset | reserved (4 bits each) (data offset used for options)
+19 - Flags                     XXX Analyse this
+20 - Window
+21 - Checksum
+22 - Urgent pointer
+23 - Options (optional)                XXX Analyse this
+24 - Data (IP length includes this)
+
+ARP (IPv4 address to MAC address LAN mode) request:
+
+        000   ff ff ff ff ff ff|f2 0b  a4 e9 70 0e|08 06|00 01|..........p.....
+              1                 2                  3     4
+        010   08 00|06|04|00 01|f2 0b  a4 e9 70 0e|c0 a8 01 0c|..........p.....
+              5     6  7  8     9                  10
+        020   00 00 00 00 00 00 c0 a8  01 01                   ..........
+              11                12
+
+4 - Hardware type (HTYPE)
+5 - Prototocol type (PTYPE)
+6 - Hardware length (HLEN)
+7 - Protocol length (PLEN)
+8 - Operation (OPER)
+9 - Sender hardware address (SHA)
+10 - Sender protocol address (SPA)
+11 - Target hardware address (THA)
+12 - Target protocol address (TPA)
+
+ARP response:
+
+        000   f2 0b a4 e9 70 0e|00 06  29 d0 66 e3|08 06|00 01|....p...).f.....
+              1                 2                  3     4
+        010   08 00|06|04|00 02|00 06  29 d0 66 e3|c0 a8 01 01|........).f.....
+              5     6  7  8     9                  10
+        020   f2 0b a4 e9 70 0e|c0 a8  01 0c|00 00 00 00 00 00 ....p...........
+              11                12           13
+        030   00 00 00 00 00 00 00 00  00 00 00 00             ............
+
+8 - Operation changes (1 = request, 2 = reply)
+13 - Appears to only be optional extra padding.
diff --git a/mmsoftware/tap-bridge/doc/tap-bridge.dia b/mmsoftware/tap-bridge/doc/tap-bridge.dia
new file mode 100644 (file)
index 0000000..787c580
--- /dev/null
@@ -0,0 +1,1121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="0.93195754289627075"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="fitwidth">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitheight">
+          <dia:int val="1"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:object type="Flowchart - Box" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="22,-2"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="21.95,-2.05;24.4425,-0.05"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="22,-2"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.3925000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#fxp0#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="23.1962,-0.9075"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="21,1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20.95,0.95;24.395,2.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="21,1"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="3.3450000000000002"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#bridge0#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="22.6725,2.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="21,4"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20.95,3.95;23.48,5.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="21,4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.4300000000000002"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#tap0#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="22.215,5.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="28,4"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="27.95,3.95;30.48,5.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="28,4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.4300000000000002"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#tap1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="29.215,5.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="17.95,8.95;21.295,10.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="18,9"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="3.2450000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#sendq0#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="19.6225,10.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="17.95,11.95;21.615,13.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="18,12"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="3.5649999999999999"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#schedq0#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="19.7825,13.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18,12.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.8996,9.45;18.05,13"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="18,12.95"/>
+        <dia:point val="16.9496,12.95"/>
+        <dia:point val="16.9496,9.95"/>
+        <dia:point val="17.9496,9.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="7"/>
+        <dia:connection handle="1" to="O4" connection="16"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="19.6225,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.5725,5.85;22.715,9.05"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="19.6225,9"/>
+        <dia:point val="19.6225,7.45"/>
+        <dia:point val="22.215,7.45"/>
+        <dia:point val="22.215,5.9"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="2"/>
+        <dia:connection handle="1" to="O2" connection="13"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="23.43,4.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="23.38,4.9;26.2112,7.05"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="23.43,4.95"/>
+        <dia:point val="25.7112,4.95"/>
+        <dia:point val="25.7112,7"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="8"/>
+        <dia:connection handle="1" to="O9" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="23.7338,7"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="23.6837,6.95;27.7387,8.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="23.7338,7"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="3.9550000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#scheduler#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="25.7112,8.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.7225,8.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="21.195,8.85;24.7725,10.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="24.7225,8.9"/>
+        <dia:point val="24.7225,9.95"/>
+        <dia:point val="21.245,9.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O9" connection="12"/>
+        <dia:connection handle="1" to="O4" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.7225,8.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="21.515,8.85;24.7725,13.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="24.7225,8.9"/>
+        <dia:point val="24.7225,12.95"/>
+        <dia:point val="21.565,12.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O9" connection="12"/>
+        <dia:connection handle="1" to="O5" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="30,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.95,8.95;33.295,10.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="30,9"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="3.2450000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000004"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#sendq1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="31.6225,10.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="30,12"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.95,11.95;33.615,13.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="30,12"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="3.5649999999999999"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#schedq1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="31.7825,13.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="28,4.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="25.2112,4.9;28.05,7.05"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="28,4.95"/>
+        <dia:point val="25.7112,4.95"/>
+        <dia:point val="25.7112,7"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="7"/>
+        <dia:connection handle="1" to="O9" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="26.7,8.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="26.65,8.85;30.05,13.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="26.7,8.9"/>
+        <dia:point val="26.7,12.95"/>
+        <dia:point val="30,12.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O9" connection="14"/>
+        <dia:connection handle="1" to="O13" connection="7"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="26.7,8.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="26.65,8.85;30.05,10.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="26.7,8.9"/>
+        <dia:point val="26.7,9.95"/>
+        <dia:point val="30,9.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O9" connection="14"/>
+        <dia:connection handle="1" to="O12" connection="7"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="33.565,12.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="33.195,9.45;34.615,13"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="33.565,12.95"/>
+        <dia:point val="34.565,12.95"/>
+        <dia:point val="34.565,9.95"/>
+        <dia:point val="33.245,9.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O13" connection="8"/>
+        <dia:connection handle="1" to="O12" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="31.6225,9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="28.715,5.85;31.6725,9.05"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="31.6225,9"/>
+        <dia:point val="31.6225,7.45"/>
+        <dia:point val="29.215,7.45"/>
+        <dia:point val="29.215,5.9"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O12" connection="2"/>
+        <dia:connection handle="1" to="O3" connection="13"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="21.97,14.25"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="21.92,14.2;29.465,21"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="21.97,14.25"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="7.4450000000000003"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="6.7000000000000002"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#Modules:
+- Layer 2/ARP filter
+- TCP fingerprint filter
+- DHCP/PPPoE servers
+- Bandwidth shaping
+...
+- Passthrough#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="22.42,15.3425"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="25.7112,8.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="25.1925,8.85;26.2112,14.3"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="25.7112,8.9"/>
+        <dia:point val="25.7112,11.575"/>
+        <dia:point val="25.6925,11.575"/>
+        <dia:point val="25.6925,14.25"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O9" connection="13"/>
+        <dia:connection handle="1" to="O19" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="21,1.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.95,1.45;21.05,5.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="21,1.95"/>
+        <dia:point val="20,1.95"/>
+        <dia:point val="20,4.95"/>
+        <dia:point val="21,4.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="7"/>
+        <dia:connection handle="1" to="O2" connection="7"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.3925,-1.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24.295,-1.55;25.4425,2.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="24.3925,-1.05"/>
+        <dia:point val="25.3925,-1.05"/>
+        <dia:point val="25.3925,1.95"/>
+        <dia:point val="24.345,1.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="8"/>
+        <dia:connection handle="1" to="O1" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Flowchart - Box" version="0" id="O23">
+      <dia:attribute name="obj_pos">
+        <dia:point val="28,1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="27.95,0.95;30.325,2.95"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="28,1"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="2.2749999999999999"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.9000000000000004"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="padding">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#LAN#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="29.1375,2.0925"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - ZigZagLine" version="1" id="O24">
+      <dia:attribute name="obj_pos">
+        <dia:point val="30.43,4.95"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="30.225,1.45;31.48,5.45"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="30.43,4.95"/>
+        <dia:point val="31.43,4.95"/>
+        <dia:point val="31.43,1.95"/>
+        <dia:point val="30.275,1.95"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="autorouting">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="start_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="22"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_length">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow_width">
+        <dia:real val="0.5"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="8"/>
+        <dia:connection handle="1" to="O23" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O25">
+      <dia:attribute name="obj_pos">
+        <dia:point val="20,22"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="20,21.4575;31.0175,22.3975"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#Diagram of the tap-bridge(8) daemon#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="20,22"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+  </dia:layer>
+</dia:diagram>
diff --git a/mmsoftware/tap-bridge/doc/tap-bridge.png b/mmsoftware/tap-bridge/doc/tap-bridge.png
new file mode 100644 (file)
index 0000000..b15a132
Binary files /dev/null and b/mmsoftware/tap-bridge/doc/tap-bridge.png differ
diff --git a/mmsoftware/tap-bridge/modules/modules.conf b/mmsoftware/tap-bridge/modules/modules.conf
new file mode 100644 (file)
index 0000000..fb62815
--- /dev/null
@@ -0,0 +1,2 @@
+tap-logger.so
+tap-passthrough.so
diff --git a/mmsoftware/tap-bridge/modules/tap-logger.c b/mmsoftware/tap-bridge/modules/tap-logger.c
new file mode 100644 (file)
index 0000000..d96e19a
--- /dev/null
@@ -0,0 +1,138 @@
+/* $Id: tap-logger.c,v 1.1 2007/12/17 19:56:07 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2007, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tap-bridge.h>
+
+
+static bool    mod_init(tap_t *, tap_t *);
+static bool    mod_recv_int(tap_t *, fnode_t *);
+static bool    mod_recv_ext(tap_t *, fnode_t *);
+static void    mod_exit(void);
+
+static void    frame_log(frame_t *, const char *);
+
+
+module_t module = {
+       mod_init,
+       mod_recv_int,
+       mod_recv_ext,
+       NULL,
+       mod_exit
+};
+
+
+static tap_t   *tap_int, *tap_ext;
+static FILE    *logfh = NULL;
+
+
+static bool
+mod_init(tap_t *tap_i, tap_t *tap_e)
+{
+       void    **udata;
+
+       tap_int = tap_i;
+       tap_ext = tap_e;
+
+       if ((udata = key_lookup("tap_logger_file")) == NULL) {
+               if ((udata = key_create("tap_logger_file")) == NULL)
+                       return false;
+               if ((logfh = fopen("/tmp/tap-filter.log", "a")) == NULL) {
+                       key_destroy("tap_logger_file");
+                       warning(errno, "fopen(/tmp/tap-filter.log)");
+                       return false;
+               }
+
+               (void) setvbuf(logfh, NULL, _IOLBF, 0);
+               *udata = logfh;
+       } else
+               logfh = *udata;
+
+       return true;
+}
+
+static bool
+mod_recv_int(tap_t *tap, fnode_t *n)
+{
+
+       frame_log(n->frame, "<");
+       fnode_destroy(n);
+       return true;
+}
+
+static bool
+mod_recv_ext(tap_t *tap, fnode_t *n)
+{
+
+       frame_log(n->frame, ">");
+       fnode_destroy(n);
+       return true;
+}
+
+static void
+frame_log(frame_t *f, const char *d)
+{
+       char    origin[32], destination[32];
+
+       if (f->size < 14) {
+               (void) fprintf(logfh, "%s Short ethernet frame: Size: %u\n",
+                   d, f->size);
+               return;
+       }
+
+       (void) macaddr_string(origin, 31, &f->origin);
+       (void) macaddr_string(destination, 31, &f->destination);
+       (void) fprintf(logfh, "%s Src: %s, Dst: %s - EType: %04x, Size: %u\n",
+           d, origin, destination, f->type, f->size);
+
+       if (f->type == 0x0806) {
+               if (f->size < 42) {
+                       (void) fprintf(logfh, " Short ARP packet\n");
+                       return;
+               }
+       }
+}
+
+static void
+mod_exit(void)
+{
+
+       /* NOOP */
+}
diff --git a/mmsoftware/tap-bridge/modules/tap-passthrough.c b/mmsoftware/tap-bridge/modules/tap-passthrough.c
new file mode 100644 (file)
index 0000000..b78d183
--- /dev/null
@@ -0,0 +1,96 @@
+/* $Id: tap-passthrough.c,v 1.1 2007/12/17 19:56:07 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2007, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tap-bridge.h>
+
+
+static bool    mod_init(tap_t *, tap_t *);
+static bool    mod_recv_int(tap_t *, fnode_t *);
+static bool    mod_recv_ext(tap_t *, fnode_t *);
+static void    mod_exit(void);
+
+
+module_t module = {
+       mod_init,
+       mod_recv_int,
+       mod_recv_ext,
+       NULL,
+       mod_exit
+};
+
+
+static tap_t   *tap_int, *tap_ext;
+
+
+static bool
+mod_init(tap_t *tap_i, tap_t *tap_e)
+{
+
+       tap_int = tap_i;
+       tap_ext = tap_e;
+
+       return true;
+}
+
+static bool
+mod_recv_int(tap_t *tap, fnode_t *n)
+{
+
+       frame_send(tap_ext, n);
+       fnode_destroy(n);
+       return false;
+}
+
+static bool
+mod_recv_ext(tap_t *tap, fnode_t *n)
+{
+
+       frame_send(tap_int, n);
+       fnode_destroy(n);
+       return false;
+}
+
+static void
+mod_exit(void)
+{
+
+       /* NOOP */
+}
diff --git a/mmsoftware/tap-bridge/tap-bridge.c b/mmsoftware/tap-bridge/tap-bridge.c
new file mode 100644 (file)
index 0000000..8699a12
--- /dev/null
@@ -0,0 +1,1549 @@
+/* $Id: tap-bridge.c,v 1.1 2007/12/17 19:56:07 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2007, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * TODO:
+ * - Fix syslog related problem when building with -DDEBUG.
+ * - Write a TCP fingerprinting prevention system.
+ */
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_ether.h>
+#include <net/if_bridgevar.h>
+#include <net/if_dl.h>
+#include <net/if_tap.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <mmlist.h>
+#include <mmpool.h>
+#include <mmhash.h>
+
+#include <tap-bridge.h>
+
+
+#define OUTMAX         1048576
+#define INMAX          262144
+
+/*
+ * These no longer necessary once my diff commited, although it might still be
+ * useful to allow configuring these.
+ */
+#define INTMAC         "f2:0b:a4:e9:70:0d"
+#define EXTMAC         "f2:0b:a4:e9:70:0e"
+
+#define MODULE_DIR     "/usr/local/lib/tap-bridge"
+#define IFBR           "bridge0"
+#define PIDFILE                "/var/run/tap-bridge.pid"
+#define TAPFILE                "/var/run/tap-bridge.if"
+#define USER           "daemon"        /* XXX */
+#define GROUP          "daemon"
+
+
+typedef struct storage {
+       hashnode_t      node;
+       char            key[64];
+       void            *udata;
+} storage_t;
+
+
+int                    main(int, char **);
+static void            usage(void);
+static int             detach(const char *, const char *, const char *);
+static int             unprivileged(const char *, const char *);
+static bool            parent_init(void);
+static void            parent_main(void);
+static void            parent_cleanup(void);
+static void            parent_sighandler(int);
+static bool            child_init(void);
+static void            child_main(void);
+static void            child_cleanup(void);
+static void            child_sighandler(int);
+static void            macaddr_align8(macaddr_t *, const uint8_t *);
+static void            macaddr_align16(macaddr_t *, const uint16_t *);
+static tap_t           *tap_open(void);
+static void            tap_close(tap_t *);
+static int             if_create(const char *);
+static int             if_destroy(const char *);
+static int             if_up(const char *);
+static int             if_down(const char *);
+static int             if_get_macaddr(uint8_t *, const char *);
+static int             if_set_macaddr(const char *, const char *);
+static int             if_get_ipaddr(in_addr_t *, in_addr_t *, in_addr_t *,
+                           const char *);
+static int             if_set_ipaddr(const char *, const char *,
+                           const char *);
+static int             bridge_add(const char *, const char *);
+static bool            frame_ctor(pnode_t *);
+static void            frame_dtor(pnode_t *);
+static void            queue_schedule(tap_t *, struct timeval *);
+static bool            queue_send(tap_t *);
+static bool            frame_receive(tap_t *);
+static bool            modules_reload(const char *, bool);
+
+
+/* Parameters */
+static char            *extmac = EXTMAC, *intmac = INTMAC, *ifeth = NULL,
+                       *ifbr = IFBR, *pidfile = PIDFILE, *tapfile = TAPFILE,
+                       *ipaddr = NULL, *netmask = NULL, *user = USER,
+                       *group = GROUP, *module_dir = MODULE_DIR;
+
+/* Flow control and book keeping */
+static int             ifsockinet = -1;
+static bool            bridgecreated = false;
+static bool            parent_exit = false;
+static pid_t           child_pid = -1;
+static bool            child_exit = false;
+static bool            schedule_now = false;
+static struct timeval  time_current, time_event;
+static pool_t          frame_pool, fnode_pool, key_pool, modnode_pool;
+static hashtable_t     storage_table;
+static list_t          module_list;
+static tap_t           *tap_ext = NULL, *tap_int = NULL;
+
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+       int     ret;
+       char    c;
+
+       openlog("tap-bridge", LOG_PERROR | LOG_PID, LOG_DAEMON);
+
+       /* Parse arguments */
+       while ((c = getopt(argc, argv, "E:I:i:b:p:t:a:n:m:")) != -1) {
+               switch (c) {
+               case 'm':       /* Modules list file */
+                       module_dir = optarg;
+                       break;
+               case 'E':       /* External tap(4) MAC address */
+                       extmac = optarg;
+                       break;
+               case 'I':       /* Internal tap(4) MAC address */
+                       intmac = optarg;
+                       break;
+               case 'i':       /* Ethernet interface */
+                       ifeth = optarg;
+                       break;
+               case 'b':       /* bridge(4) interface */
+                       ifbr = optarg;
+                       break;
+               case 'p':       /* PID file */
+                       pidfile = optarg;
+                       break;
+               case 't':       /* tap(4) interface file */
+                       tapfile = optarg;
+                       break;
+               case 'a':       /* IP address to assign to external tap(4) */
+                       ipaddr = optarg;
+                       break;
+               case 'n':       /* Netmask to assign to exnternal tap(4) */
+                       netmask = optarg;
+                       break;
+               case 'u':       /* User to drop privileges to */
+                       user = optarg;
+                       break;
+               case 'g':       /* Group to drop privileges to */
+                       group = optarg;
+                       break;
+               case '?':
+                       /* FALLTHROUGH */
+               default:
+                       usage();
+                       return EXIT_FAILURE;
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (ifeth == NULL) {
+               usage();
+               return EXIT_FAILURE;
+       }
+       if (ipaddr != NULL || netmask != NULL) {
+               if (ipaddr == NULL || netmask == NULL) {
+                       usage();
+                       return EXIT_FAILURE;
+               }
+       }
+
+       /* Initialize privileged resources */
+       if (!parent_init())
+               return EXIT_FAILURE;
+
+       /* Launch child unprivileged process */
+       ret = EXIT_SUCCESS;
+       if (!child_init()) {
+               ret = EXIT_FAILURE;
+               goto out;
+       }
+
+       /*
+        * Wait until child exits before cleaning up privileged resources and
+        * exiting.
+        */
+       parent_main();
+
+out:
+       parent_cleanup();
+       return ret;
+}
+
+static void
+usage(void)
+{
+
+       warning(0,
+           "Usage: %s -i <ethernet-iface> [-E <ext-tap-mac>]\n"
+           "    [-I <int-tap-mac>] [-b <bridge-iface>] [-p <pidfile>]\n"
+           "    [-t <tapfile>] [-a <ipaddr> -n <netmask>]\n"
+           "    [-m <modulesdir>]\n", getprogname());
+}
+
+/*
+ * Become a background daemon and write our PID file.
+ * Returns 0 on success or -1 on error.
+ */
+static pid_t
+detach(const char *pidfile, const char *tapfile, const char *tapiface)
+{
+       pid_t   pid;
+       int     fd;
+
+       if ((pid = fork()) == -1)
+               return -1;
+       if (pid != 0)
+               exit(EXIT_SUCCESS);
+
+       /* Create PID file */
+       if ((fd = open(pidfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
+               char    str[16];
+
+               (void) snprintf(str, 15, "%d\n", getpid());
+               if (write(fd, str, strlen(str)) == -1 || close(fd) == -1) {
+                       warning(errno, "Error writing PID file [%s]", pidfile);
+                       return -1;
+               }
+       } else {
+               warning(errno, "Error creating PID file [%s]", pidfile);
+               return -1;
+       }
+
+       /* Create tap(4) interface file */
+       if ((fd = open(tapfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
+               char    str[16];
+
+               (void) snprintf(str, 15, "%s\n", tapiface);
+               if (write(fd, str, strlen(str)) == -1 || close(fd) == -1) {
+                       warning(errno, "Error writing tap file [%s]", tapfile);
+                       return -1;
+               }
+       } else {
+               warning(errno, "Error creating tap file [%s]", tapfile);
+               return -1;
+       }
+
+       /* Be paranoid, redirect our stdio file descriptors to null(4) */
+       (void) setsid();
+       (void) chdir("/");
+       if ((fd = open("/dev/null", O_RDWR)) != -1) {
+               (void) dup2(fd, STDIN_FILENO);
+               (void) dup2(fd, STDOUT_FILENO);
+               /* Keep stderr */
+               if (fd > STDERR_FILENO)
+                       (void) close(fd);
+       } else {
+               warning(errno, "Error opening null(4) device");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+unprivileged(const char *user, const char *group)
+{
+       struct passwd   *pwd;
+       uid_t           uid;
+       struct group    *grp;
+       gid_t           gid;
+
+       if ((pwd = getpwnam(user)) == NULL) {
+               warning(errno, "getpwnam");
+               return -1;
+       }
+       uid = pwd->pw_uid;
+
+       if ((grp = getgrnam(group)) == NULL) {
+               warning(errno, "getgrnam");
+               return -1;
+       }
+       gid = grp->gr_gid;
+
+       if (setgid(gid) == -1) {
+               warning(errno, "setgid");
+               return -1;
+       }
+       if (setegid(gid) == -1) {
+               warning(errno, "setegid");
+               return -1;
+       }
+       if (setgroups(0, &gid) == -1) {
+               warning(errno, "setgroups");
+               return -1;
+       }
+
+       if (setuid(uid) == -1) {
+               warning(errno, "setuid");
+               return -1;
+       }
+       if (seteuid(uid) == -1) {
+               warning(errno, "seteuid");
+               return -1;
+       }
+
+       return 0;
+}
+
+static bool
+parent_init(void)
+{
+
+       /* We need this socket for interface ioctls */
+       if ((ifsockinet = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               warning(errno, "socket(ifsockinet)");
+               goto err;
+       }
+
+       /* Remove address from ethernet interface and down it */
+       if (if_set_ipaddr(ifeth, "0.0.0.0", "0.255.255.255") == -1 ||
+           if_down(ifeth) == -1)
+              goto err;
+
+       /* Create a pair of tap(4) devices/interfaces */
+       if ((tap_int = tap_open()) == NULL ||
+           (tap_ext = tap_open()) == NULL)
+               goto err;
+
+       /* Assign MAC addresses to them */
+       /*
+        * XXX Only do if specified, use default addresses otherwise.
+        * We use these because of a bug in tap(4) which can cause two tap
+        * interfaces to have the same MAC address if they're created during
+        * the same second,
+        */
+       if (if_set_macaddr(tap_ext->name, extmac) == -1 ||
+           if_set_macaddr(tap_int->name, intmac) == -1)
+               goto err;
+
+       /* Obtain/remember MAC addresses */
+       {
+               uint8_t mac[ETHER_ADDR_LEN];
+
+               if (if_get_macaddr(mac, tap_ext->name) == -1)
+                       goto err;
+               macaddr_align8(&tap_ext->mac, mac);
+
+               if (if_get_macaddr(mac, tap_int->name) == -1)
+                       goto err;
+               macaddr_align8(&tap_int->mac, mac);
+       }
+       /* Assign IP address/netmask to external interface if wanted */
+       if (ipaddr != NULL) {
+               if (if_set_ipaddr(tap_ext->name, netmask, ipaddr) == -1)
+                       goto err;
+       }
+
+       /* Set non-blocking mode */
+       if (fcntl(tap_ext->fd, F_SETFL, O_NONBLOCK) == -1 ||
+           fcntl(tap_int->fd, F_SETFL, O_NONBLOCK) == -1) {
+               warning(errno, "fcntl(O_NONBLOCK)");
+               goto err;
+       }
+
+       /* Create bridge interface */
+       if (if_create(ifbr) == -1)
+               goto err;
+       bridgecreated = true;
+       if (bridge_add(ifbr, ifeth) == -1 ||
+           bridge_add(ifbr, tap_int->name) == -1 ||
+           if_up(tap_int->name) == -1 ||
+           if_up(ifeth) == -1 ||
+           if_up(ifbr) == -1)
+               goto err;
+       bridgecreated = true;
+
+       if (detach(pidfile, tapfile, tap_ext->name) == -1)
+               goto err;
+
+       /* Setup parent sighandler */
+       {
+               struct sigaction        act;
+
+               act.sa_handler = parent_sighandler;
+               (void) sigemptyset(&act.sa_mask);
+               act.sa_flags = 0;
+               if (sigaction(SIGTERM, &act, NULL) == -1 ||
+                   sigaction(SIGINT, &act, NULL) == -1 ||
+                   sigaction(SIGCHLD, &act, NULL) == -1 ||
+                   sigaction(SIGHUP, &act, NULL) == -1) {
+                       warning(errno, "sigaction(parent)");
+                       goto err;
+               }
+               act.sa_handler = SIG_IGN;
+               (void) sigaction(SIGTTOU, &act, NULL);
+               (void) sigaction(SIGTTIN, &act, NULL);
+               (void) sigaction(SIGTSTP, &act, NULL);
+       }
+
+       /* This application is latency-sensitive */
+       if (setpriority(PRIO_PGRP, 0, -10) == -1) {
+               warning(errno, "setpriority");
+               goto err;
+       }
+
+       return true;
+
+err:
+       parent_cleanup();
+       return false;
+}
+
+/*
+ * Simply loop until our child process dies.
+ */
+static void
+parent_main(void)
+{
+       sigset_t        set;
+
+       warning(0, "Parent process started");
+
+       (void) sigemptyset(&set);
+
+       while (!parent_exit)
+               (void) sigsuspend(&set);
+
+       warning(0, "Parent process exiting");
+}
+
+static void
+parent_sighandler(int sig)
+{
+
+       switch (sig) {
+       case SIGTERM:
+               /* FALLTHROUGH */
+       case SIGINT:
+               /* FALLTHROUGH */
+       case SIGHUP:
+               if (child_pid != -1) {
+                       warning(0, "Parent forwarding signal %d to child",
+                           sig);
+                       (void) kill(child_pid, sig);
+               }
+               break;
+       case SIGCHLD:
+               for (;;) {
+                       pid_t   pid;
+                       int     status = 0;
+
+                       while ((pid = wait3(&status, WNOHANG, NULL)) == -1 &&
+                           errno == EINTR) ;
+                       if (pid < 1)
+                               break;
+                       if (!WIFEXITED(status))
+                               continue;
+                       warning(0, "Parent detected child exit");
+                       parent_exit = true;
+                       break;
+               }
+               break;
+       }
+}
+
+static void
+parent_cleanup(void)
+{
+
+       (void) if_down(ifeth);
+       if (bridgecreated) {
+               (void) if_down(ifbr);
+               (void) if_destroy(ifbr);
+       }
+       if (tap_int != NULL) {
+               (void) if_down(tap_int->name);
+               tap_close(tap_int);
+       }
+       if (tap_ext != NULL) {
+               (void) if_down(tap_ext->name);
+               tap_close(tap_ext);
+       }
+       if (ifsockinet != -1)
+               (void) close(ifsockinet);
+}
+
+static bool
+child_init(void)
+{
+
+       /*
+        * Create a child process to which we delegate the following code.
+        * We use this for privilege separation.  The parent simply waits
+        * until the child exits to then clean up resources.
+        */
+       {
+               pid_t   pid;
+
+               if ((pid = fork()) == -1) {
+                       warning(errno, "fork(child)");
+                       return false;
+               }
+               if (pid != 0) {
+                       child_pid = pid;
+                       return true;
+               }
+       }
+
+       /*
+        * We're the child, drop privileges and continue.
+        */
+       if (unprivileged(user, group) == -1)
+               goto err;
+
+       /* Setup child sighandler */
+       {
+               struct sigaction        act;
+
+               act.sa_handler = child_sighandler;
+               (void) sigemptyset(&act.sa_mask);
+               act.sa_flags = 0;
+               if (sigaction(SIGTERM, &act, NULL) == -1 ||
+                   sigaction(SIGINT, &act, NULL) == -1 ||
+                   sigaction(SIGALRM, &act, NULL) == -1 ||
+                   sigaction(SIGHUP, &act, NULL) == -1) {
+                       warning(errno, "sigaction(child)");
+                       goto err;
+               }
+               act.sa_handler = SIG_IGN;
+               (void) sigaction(SIGCHLD, &act, NULL);
+       }
+
+       /* Setup our pools */
+       {
+               size_t pagesize;
+
+               pagesize = sysconf(_SC_PAGESIZE);
+               if (!pool_init(&frame_pool, "frame_pool", malloc, free,
+                   frame_ctor, frame_dtor, sizeof(frame_t),
+                   pagesize / sizeof(frame_t), 1, 0)) {
+                       warning(errno, "pool_init(frame_pool)");
+                       goto err;
+               }
+               if (!pool_init(&fnode_pool, "fnode_pool", malloc, free,
+                   NULL, NULL, sizeof(fnode_t), pagesize / sizeof(fnode_t),
+                   1, 0)) {
+                       warning(errno, "pool_init(fnode_pool)");
+                       goto err;
+               }
+               if (!pool_init(&key_pool, "key_pool", malloc, free, NULL,
+                   NULL, sizeof(storage_t), pagesize / sizeof(storage_t),
+                   1, 0)) {
+                       warning(errno, "pool_init(key_pool)");
+                       goto err;
+               }
+               if (!pool_init(&modnode_pool, "modnode_pool", malloc, free,
+                   NULL, NULL, sizeof(modnode_t),
+                   pagesize / sizeof(modnode_t), 1, 0)) {
+                       warning(errno, "pool_init(modnode_pool)");
+                       goto err;
+               }
+       }
+       /* And our keys hash table */
+       if (!hashtable_init(&storage_table, "storage_table",
+           HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, malloc, free, memcmp,
+           hashtable_hash, true)) {
+               warning(errno, "hashtable_init(storage_table)");
+               goto err;
+       }
+       /* List of loaded modules */
+       DLIST_INIT(&module_list);
+
+       /* Start timer */
+       {
+               struct itimerval        itv;
+
+               timerclear(&time_event);
+               itv.it_interval.tv_sec = 0;
+               itv.it_interval.tv_usec = 1000000 / TIMER_HZ;
+               itv.it_value = itv.it_interval;
+               if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
+                       warning(errno, "setitimer");
+                       goto err;
+               }
+       }
+
+       /* Load modules */
+       if (!modules_reload(module_dir, true)) {
+               warning(errno, "modules_reload");
+               goto err;
+       }
+
+       child_main();
+       child_cleanup();
+       exit(EXIT_SUCCESS);
+
+err:
+       exit(EXIT_SUCCESS);
+}
+
+static void
+child_main(void)
+{
+       struct pollfd   fds[2];
+
+       warning(0, "Child unprivileged process started");
+
+       fds[0].fd = tap_ext->fd;
+       fds[1].fd = tap_int->fd;
+       fds[0].events = fds[1].events = POLLIN;
+       fds[0].revents = fds[1].revents = 0;
+       while (!child_exit) {
+
+               /*
+                * If a scheduling round was triggered, transfer all expired
+                * nodes to the send list and update timeout to soonest to be
+                * expired node.
+                */
+               if (schedule_now) {
+                       struct timeval  ttv;
+
+                       schedule_now = false;
+
+                       /* Start with initial timeout of one minute */
+                       ttv = time_current;
+                       ttv.tv_sec += 60;
+
+                       queue_schedule(tap_ext, &ttv);
+                       queue_schedule(tap_int, &ttv);
+
+                       /* Set new timeout to soonest expireing node */
+                       time_event = ttv;
+               }
+
+               /*
+                * For performance we process as much data as possible
+                * before polling.  Prioritize output, and use a limit to
+                * prevent starvation of one direction.
+                */
+
+               /*
+                * Send out some data from our send queue if any.
+                * What resides here should already have been sanity checked
+                * by any user code, as it was queued by user code for
+                * sending.
+                */
+               if (!queue_send(tap_ext) && errno != EAGAIN &&
+                   errno != EHOSTDOWN) {
+                       warning(errno, "queue_send(tap_ext)");
+                       break;
+               }
+               if (!queue_send(tap_int) && errno != EAGAIN &&
+                   errno != EHOSTDOWN) {
+                       warning(errno, "queue_send(tap_int)");
+                       break;
+               }
+
+               /*
+                * Read in data if any, allocating new frames for it and
+                * feeding them to user code, which may filter, modify, drop
+                * or respond by sending response packets.  We also fill in a
+                * few fields of the frame_t structure prior to passing them
+                * to user code.
+                */
+               if (!frame_receive(tap_int) && errno != EAGAIN &&
+                   errno != EHOSTDOWN) {
+                       warning(errno, "frame_receive(tap_int)");
+                       break;
+               }
+               if (!frame_receive(tap_ext) && errno != EAGAIN &&
+                   errno != EHOSTDOWN) {
+                       warning(errno, "frame_receive(tap_ext)");
+                       break;
+               }
+
+               /* Are we supposed to quit? */
+               if (child_exit)
+                       break;
+
+               /* Don't poll yet if we have more events to process. */
+               if (schedule_now && errno != EHOSTDOWN)
+                       continue;
+
+               /*
+                * Poll and continue loop if any interesting data.
+                */
+               for (;;) {
+                       int     ret;
+
+                       ret = poll(fds, 2, -1);
+                       if (ret == 0) {
+                               if (schedule_now)
+                                       break;
+                               continue;
+                       }
+                       if (ret == -1) {
+                               if (errno == EINTR)
+                                       break;
+                               warning(errno, "poll");
+                               goto out;
+                       }
+                       if ((fds[0].revents & (POLLHUP | POLLERR)) != 0 ||
+                           (fds[1].revents & (POLLHUP | POLLERR)) != 0) {
+                               warning(errno, "poll");
+                               goto out;
+                       }
+                       if ((fds[0].revents & POLLIN) != 0 ||
+                           (fds[1].revents & POLLIN) != 0)
+                               break;
+               }
+       }
+
+out:
+       warning(0, "Child unprivileged process exiting");
+}
+
+static void
+child_sighandler(int sig)
+{
+       modnode_t       *n;
+
+       switch (sig) {
+       case SIGTERM:
+               /* FALLTHROUGH */
+       case SIGINT:
+               warning(0, "Child received SIGTERM; exiting");
+               child_exit = true;
+               break;
+       case SIGALRM:
+               /*
+                * Update current time value to prevent having to invoke
+                * gettimeofday(2) at a too high frequency.
+                * Call modules callback functions if any, and verify if any
+                * scheduled to be sent packets should now be sent, setting
+                * the schedule_now flag if so.
+                */
+               (void) gettimeofday(&time_current, NULL);
+               DLIST_FOREACH(&module_list, n) {
+                       if (n->module->tick != NULL)
+                               n->module->tick(&time_current);
+               }
+               if (timerisset(&time_event) &&
+                   timercmp(&time_event, &time_current, <))
+                       schedule_now = true;
+               break;
+       case SIGHUP:
+               warning(0, "Child received SIGHUP; reloading modules");
+               /* Reload modules specified in configuration file */
+               if (!modules_reload(module_dir, true))
+                       warning(errno, "modules_reload");
+               break;
+       }
+}
+
+static void
+child_cleanup(void)
+{
+
+       (void) modules_reload(module_dir, false);
+}
+
+/*
+ * Converts the ETHER_ADDR_LEN sized byte string to a 64-bit aligned macaddr_t
+ * type for better performance.  Since this consists of an internal format,
+ * endian byte order is unimportant.  This will be used for comparisions
+ * against other macaddr_t.  To simplify things even more, accessing a
+ * macaddr_t as an uint8_t array should return back the size bytes in the same
+ * order they previously were, which may be useful for operations such as
+ * logging.
+ */
+static void
+macaddr_align8(macaddr_t *a, const uint8_t *b)
+{
+       register const uint8_t  *rb = b;
+       register uint8_t        *rab = (uint8_t *)a;
+
+       /* Unroll loop */
+       rab[0] = rb[0];
+       rab[1] = rb[1];
+       rab[2] = rb[2];
+       rab[3] = rb[3];
+       rab[4] = rb[4];
+       rab[5] = rb[5];
+       rab[6] = 0x00;
+       rab[7] = 0x00;
+}
+
+/*
+ * This optimized version deals with known to be 16-bit aligned MAC addresses,
+ * as is the case with origin/destination addresses received in an ethernet
+ * frame.
+ */
+static void
+macaddr_align16(macaddr_t *a, const uint16_t *b)
+{
+       register const uint16_t *rb = b;
+       register uint16_t       *rab = (uint16_t *)a;
+
+       /* Unroll loop */
+       rab[0] = rb[0];
+       rab[1] = rb[1];
+       rab[2] = rb[2];
+       rab[3] = 0x0000;
+}
+
+static tap_t *
+tap_open(void)
+{
+       int             fd = -1;
+       tap_t           *tap = NULL;
+       struct ifreq    iface;
+
+       if ((fd = open("/dev/tap", O_RDWR)) == -1) {
+               warning(errno, "open(/dev/tap)");
+               goto err;
+       }
+       if ((tap = malloc(sizeof(tap_t))) == NULL) {
+               warning(errno, "malloc(tap_t)");
+               goto err;
+       }
+       if (ioctl(fd, TAPGIFNAME, &iface) == -1) {
+               warning(errno, "ioctl(TAPGIFNAME)");
+               goto err;
+       }
+
+       tap->fd = fd;
+       if ((tap->name = strdup(iface.ifr_name)) == NULL) {
+               warning(errno, "stddup(iface.ifr_name)");
+               goto err;
+       }
+
+       DLIST_INIT(&tap->schedq);
+       DLIST_INIT(&tap->sendq);
+
+       return tap;
+
+err:
+       if (fd != -1)
+               (void) close(fd);
+       if (tap != NULL) {
+               if (tap->name != NULL)
+                       free(tap->name);
+               free(tap);
+       }
+       return NULL;
+}
+
+static void
+tap_close(tap_t *tap)
+{
+
+       assert(tap != NULL);
+       assert(tap->name != NULL);
+       assert(tap->fd != -1);
+       free(tap->name);
+       (void) close(tap->fd);
+       free(tap);
+}
+
+static int
+if_create(const char *iface)
+{
+       struct ifreq    ifr;
+       int             error;
+
+       (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+       if ((error = ioctl(ifsockinet, SIOCIFCREATE, &ifr)) == -1)
+               warning(errno, "if_create(%s)", iface);
+       return error;
+}
+
+static int
+if_destroy(const char *iface)
+{
+       struct ifreq    ifr;
+       int             error;
+
+       (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+       if ((error = ioctl(ifsockinet, SIOCIFDESTROY, &ifr)) == -1)
+               warning(errno, "if_destroy(%s)", iface);
+       return error;
+}
+
+static int
+if_up(const char *iface)
+{
+       struct ifreq    ifr;
+       int             error;
+
+       (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+       if (ioctl(ifsockinet, SIOCGIFFLAGS, &ifr) != 0)
+               return -1;
+       ifr.ifr_flags |= IFF_UP;
+       if ((error = ioctl(ifsockinet, SIOCSIFFLAGS, &ifr)) == -1)
+               warning(errno, "if_up(%s)", iface);
+       return error;
+}
+
+static int
+if_down(const char *iface)
+{
+       struct ifreq    ifr;
+       int             error;
+
+       (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+       if (ioctl(ifsockinet, SIOCGIFFLAGS, &ifr) != 0)
+               return -1;
+       ifr.ifr_flags &= ~IFF_UP;
+       if ((error = ioctl(ifsockinet, SIOCSIFFLAGS, &ifr)) == -1)
+               warning(errno, "if_down(%s)", iface);
+       return error;
+}
+
+static int
+if_get_macaddr(uint8_t *buf, const char *iface)
+{
+       struct ifaddrs  *ifa = NULL, *p;
+       int             error = 0;
+
+       if ((error = getifaddrs(&ifa)) == -1)
+               goto out;
+       for (p = ifa; p != NULL; p = p->ifa_next) {
+               if (strcmp(iface, p->ifa_name) == 0)
+                       break;
+       }
+       if (p == NULL) {
+               error = -1;
+               errno = ENOENT;
+               goto out;
+       }
+       if (p->ifa_addr->sa_family != AF_LINK) {
+               error = -1;
+               errno = EAFNOSUPPORT;
+               goto out;
+       }
+
+       (void) memcpy(buf, p->ifa_addr->sa_data, ETHER_ADDR_LEN);
+
+out:
+       if (ifa != NULL)
+               freeifaddrs(ifa);
+
+       if (error != 0)
+               warning(errno, "if_get_macaddr(%s)", iface);
+       return error;
+}
+
+/*
+ * This is tap(4) specific for now.  Hopefully a generic ethernet ioctl(2)
+ * will eventually be provided for this.
+ */
+static int
+if_set_macaddr(const char *iface, const char *addr)
+{
+       struct ifaliasreq       ifra;
+       struct ether_addr       *eaddr;
+
+       (void) memset(&ifra, 0x00, sizeof(struct ifaliasreq));
+
+       if ((eaddr = ether_aton(addr)) == NULL)
+               goto err;
+       (void) memcpy(ifra.ifra_addr.sa_data, eaddr->ether_addr_octet,
+           ETHER_ADDR_LEN);
+       (void) strncpy(ifra.ifra_name, iface, sizeof(ifra.ifra_name) - 1);
+       ifra.ifra_addr.sa_family = AF_LINK;
+       ifra.ifra_addr.sa_len = sizeof(struct sockaddr_dl);
+
+       if (ioctl(ifsockinet, SIOCSIFPHYADDR, &ifra) == -1)
+               goto err;
+       return 0;
+
+err:
+       warning(errno, "if_set_macaddr(%s, %s)", iface, addr);
+       return -1;
+}
+
+static int
+if_get_ipaddr(in_addr_t *addr, in_addr_t *mask, in_addr_t *braddr,
+    const char *iface)
+{
+       struct ifreq            ifr;
+       struct sockaddr_in      *sin = (struct sockaddr_in *)&ifr.ifr_addr;
+
+       (void) memset(&ifr, 0x00, sizeof(struct ifreq));
+       (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+
+       ifr.ifr_addr.sa_family = AF_INET;
+       ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
+       if (ioctl(ifsockinet, SIOCGIFADDR, &ifr) == -1)
+               goto err;
+       *addr = sin->sin_addr.s_addr;
+       sin->sin_addr.s_addr = 0x00000000;
+
+       ifr.ifr_addr.sa_family = AF_INET;
+       ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
+       if (ioctl(ifsockinet, SIOCGIFNETMASK, &ifr) == -1)
+               goto err;
+       *mask = sin->sin_addr.s_addr;
+       sin->sin_addr.s_addr = 0x00000000;
+
+       ifr.ifr_addr.sa_family = AF_INET;
+       ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
+       if (ioctl(ifsockinet, SIOCGIFBRDADDR, &ifr) == -1)
+               goto err;
+       *braddr = sin->sin_addr.s_addr;
+
+       return 0;
+
+err:
+       warning(errno, "if_get_ipaddr(%s)", iface);
+       return -1;
+}
+
+static int
+if_set_ipaddr(const char *iface, const char *netmask, const char *address)
+{
+       struct ifaliasreq       ifra;
+       struct sockaddr_in      *sin;
+
+       (void) memset(&ifra, 0x00, sizeof(struct ifaliasreq));
+       (void) strncpy(ifra.ifra_name, iface, sizeof(ifra.ifra_name) - 1);
+
+       sin = (struct sockaddr_in *)&ifra.ifra_addr;
+       if (inet_pton(AF_INET, address, &sin->sin_addr) != 1)
+               goto err;
+       ifra.ifra_addr.sa_family = AF_INET;
+       ifra.ifra_addr.sa_len = sizeof(struct sockaddr_in);
+
+       sin = (struct sockaddr_in *)&ifra.ifra_mask;
+       if (inet_pton(AF_INET, netmask, &sin->sin_addr) != 1)
+               goto err;
+       ifra.ifra_mask.sa_family = AF_INET;
+       ifra.ifra_mask.sa_len = sizeof(struct sockaddr_in);
+
+       if (ioctl(ifsockinet, SIOCAIFADDR, &ifra) == -1)
+               goto err;
+
+       return 0;
+
+err:
+       warning(errno, "if_set_ipaddr(%s)", iface);
+       return -1;
+}
+
+static int
+bridge_add(const char *br, const char *iface)
+{
+       struct ifbreq   req;
+       struct ifdrv    ifd;
+       int             error;
+
+       (void) memset(&req, 0x00, sizeof(struct ifbreq));
+       (void) strncpy(req.ifbr_ifsname, iface, sizeof(req.ifbr_ifsname) - 1);
+
+       (void) memset(&ifd, 0x00, sizeof(struct ifdrv));
+       (void) strncpy(ifd.ifd_name, br, sizeof(ifd.ifd_name) - 1);
+       ifd.ifd_cmd = BRDGADD;
+       ifd.ifd_len = sizeof(struct ifbreq);
+       ifd.ifd_data = &req;
+
+       if ((error = ioctl(ifsockinet, SIOCSDRVSPEC, &ifd)) == -1)
+               warning(errno, "bridge_add(%s, %s)", br, iface);
+       return error;
+}
+
+static bool
+frame_ctor(pnode_t *pnod)
+{
+       frame_t *f = (frame_t *)pnod;
+
+       if ((f->data = malloc(FRAMESIZE)) == NULL)
+               return false;
+
+       return true;
+}
+
+static void
+frame_dtor(pnode_t *pnod)
+{
+       frame_t *f = (frame_t *)pnod;
+
+       if (f->data != NULL)
+               free(f->data);
+}
+
+static void
+queue_schedule(tap_t *tap, struct timeval *ttv)
+{
+       fnode_t         *n, *t;
+       struct timeval  *tv;
+
+       tv = ttv;
+       for (n = (fnode_t *)DLIST_TOP(&tap->schedq); n != NULL; n = t) {
+               t = (fnode_t *)DLIST_NEXT((node_t *)n);
+               if (timercmp(&n->scheduled, &time_current, <))
+                       DLIST_SWAP(&tap->sendq, &tap->schedq, (node_t *)n,
+                           false);
+               else if (timercmp(&n->scheduled, tv, <))
+                       tv = &n->scheduled;
+       }
+
+       if (tv != ttv)
+               *ttv = *tv;
+}
+
+static bool
+queue_send(tap_t *tap)
+{
+       node_t  *n, *t;
+       size_t  count;
+
+       count = 0;
+       for (n = DLIST_TOP(&tap->sendq);
+           !child_exit && count < OUTMAX && n != NULL;
+           n = t) {
+               fnode_t         *fn = (fnode_t *)n;
+               ssize_t         ret;
+               struct iovec    iov[2];
+
+               t = DLIST_NEXT(n);
+
+               iov[0].iov_base = fn->frame->data;
+               iov[0].iov_len = 14;
+               iov[1].iov_base = &fn->frame->data[16];
+               iov[1].iov_len = fn->frame->size - 16;
+               while ((ret = writev(tap->fd, iov, 2)) == -1 &&
+                   errno == EINTR) ;
+               /* On error leave pending frame on queue */
+               if (ret != fn->frame->size - 2)
+                       return false;
+
+               count += fn->frame->size;
+               DLIST_UNLINK(&tap->sendq, n);
+               fnode_destroy(fn);
+       }
+
+       errno = EAGAIN;
+       return false;
+}
+
+static bool
+frame_receive(tap_t *tap)
+{
+       size_t  count;
+       ssize_t len;
+       frame_t *f;
+
+       count = 0;
+       for (f = NULL; !child_exit && count < INMAX; count += len) {
+               struct iovec    iov[2];
+               modnode_t       *n;
+
+               if ((f = (frame_t *)pool_alloc(&frame_pool, false)) == NULL) {
+                       errno = ENOMEM;
+                       warning(errno, "pool_alloc");
+                       return false;
+               }
+
+               ((uint16_t *)f->data)[7] = 0x0000;
+               iov[0].iov_base = f->data;
+               iov[0].iov_len = 14;
+               iov[1].iov_base = &f->data[16];
+               iov[1].iov_len = FRAMESIZE - 14;
+               while ((len = readv(tap->fd, iov, 2)) == -1
+                   && errno == EINTR) ;
+
+               if (len == -1 || len == 0) {
+                       (void) pool_free((pnode_t *)f);
+                       return false;
+               }
+
+               macaddr_align16(&f->destination, (uint16_t *)f->data);
+               macaddr_align16(&f->origin, &((uint16_t *)f->data)[3]);
+               f->received = time_current;
+               f->size = len + 2;
+               f->type = ntohs(((uint16_t *)f->data)[6]);
+
+               /*
+                * To every module receive function we pass a new fnode_t
+                * which it becomes responsible for.
+                */
+               DLIST_FOREACH(&module_list, n) {
+                       bool    (*recv)(tap_t *, fnode_t *);
+                       fnode_t *fn;
+
+                       if ((recv = (tap == tap_int ? n->module->recv_int :
+                           n->module->recv_ext)) == NULL)
+                               continue;
+
+                       if ((fn = fnode_new(f)) == NULL) {
+                               warning(errno, "fnode_new");
+                               continue;
+                       }
+                       if (!recv(tap, fn))
+                               break;
+               }
+       }
+
+       return true;
+}
+
+static bool
+modules_reload(const char *fp, bool load)
+{
+       bool            ret = false;
+       FILE            *fh = NULL;
+       modnode_t       *n, *t;
+       char            buf[256], path[1024];
+       void            *handle = NULL;
+       sigset_t        set, oset;
+
+       (void) sigemptyset(&set);
+       (void) sigaddset(&set, SIGALRM);
+       (void) sigaddset(&set, SIGTERM);
+       (void) sigaddset(&set, SIGHUP);
+       oset = set;
+       (void) sigprocmask(SIG_BLOCK, &set, NULL);
+
+       /* Unload all modules in reverse order */
+       for (n = (modnode_t *)DLIST_BOTTOM(&module_list); n != NULL; n = t) {
+               t = (modnode_t *)DLIST_PREV((pnode_t *)n);
+
+               n->module->exit();
+               (void) dlclose(n->handle);
+               DLIST_UNLINK(&module_list, (node_t *)n);
+               (void) pool_free((pnode_t *)n);
+       }
+       if (!load) {
+               ret = true;
+               goto out;
+       }
+
+       (void) snprintf(path, sizeof(path) - 1, "%s/%s", fp, "modules.conf");
+       if ((fh = fopen(path, "r")) == NULL) {
+               warning(errno, "fopen");
+               goto out;
+       }
+
+       /* Attempt to reload every specified module */
+       while (fgets(buf, sizeof(buf) - 1, fh) != NULL) {
+               void            *sym;
+               char            *cptr;
+
+               if ((cptr = strchr(buf, '\n')) != NULL)
+                       *cptr = '\0';
+               (void) snprintf(path, sizeof(path) - 1, "%s/%s", fp, buf);
+               if ((handle = dlopen(path, O_RDONLY)) == NULL) {
+                       warning(errno, "dlopen(%s) - %s", buf, dlerror());
+                       goto out;
+               }
+               if ((sym = dlsym(handle, "module")) == NULL) {
+                       warning(errno, "dlsym(%s, module) - %s",
+                           buf, dlerror());
+                       goto out;
+               }
+               if ((n = (modnode_t *)pool_alloc(&modnode_pool, false))
+                   == NULL)
+                       goto out;
+
+               n->handle = handle;
+               handle = NULL;
+               n->module = sym;
+
+               if (n->module->init == NULL || n->module->exit == NULL) {
+                       warning(errno, "module(%s) - No init()/exit()!", buf);
+                       goto out;
+               }
+               if (!n->module->init(tap_int, tap_ext)) {
+                       warning(errno, "module(%s) - init() failed!", buf);
+                       goto out;
+               }
+
+               DLIST_APPEND(&module_list, (node_t *)n);
+               n = NULL;
+       }
+
+       ret = true;
+
+out:
+       if (n != NULL)
+               (void) pool_free((pnode_t *)n);
+       if (handle != NULL)
+               (void) dlclose(handle);
+       if (fh != NULL)
+               (void) fclose(fh);
+
+       (void) sigprocmask(SIG_UNBLOCK, &oset, NULL);
+       return ret;
+}
+
+
+/*
+ * EXPORTED FUNCTIONS
+ */
+
+/*
+ * Creates space for a new frame.  The caller should fill the frame itself.
+ * Returns NULL on error.
+ */
+frame_t *
+frame_new(void)
+{
+       frame_t *f;
+
+       if ((f = (frame_t *)pool_alloc(&frame_pool, false)) != NULL)
+               f->refcount = 0;
+
+       return f;
+}
+
+/*
+ * Creates a new frame node.  These may be manipulated as wanted.  A frame is
+ * never freed unless all created fnode_t objects tried to a frame_t are
+ * destroyed.  This is a useful block to use custom queueing with frames.
+ */
+fnode_t *
+fnode_new(frame_t *f)
+{
+       fnode_t *n;
+
+       assert(f != NULL);
+
+       if ((n = (fnode_t *)pool_alloc(&fnode_pool, false)) != NULL) {
+               f->refcount++;
+               n->frame = f;
+       }
+
+       return n;
+}
+
+/*
+ * Destroys the specified frame node.  If all frame nodes tied to the
+ * underlaying frame_t the frame is also destroyed.
+ */
+void
+fnode_destroy(fnode_t *n)
+{
+
+       assert(n != NULL && n->frame != NULL);
+
+       if (--(n->frame->refcount) == 0)
+               (void) pool_free((pnode_t *)n->frame);
+       (void) pool_free((pnode_t *)n);
+}
+
+size_t
+macaddr_string(char *str, size_t len, macaddr_t *a)
+{
+       register uint8_t        *rb = (uint8_t *)a;
+
+       return snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
+           rb[0], rb[1], rb[2], rb[3], rb[4], rb[5]);
+}
+
+/*
+ * Send the specified frame as soon as possible.
+ * A new fnode_t object is internally created to queue this node for sending.
+ * The sender thus should still destroy its fnode_t unless it intends to send
+ * the same frame again.
+ */
+void
+frame_send(tap_t *tap, fnode_t *n)
+{
+       fnode_t *fn;
+
+       assert(tap != NULL && n != NULL);
+
+       if ((fn = fnode_new(n->frame)) != NULL)
+               DLIST_APPEND(&tap->sendq, (node_t *)fn);
+       /* Flush queue immediately to minimize latency */
+       (void) queue_send(tap);
+}
+
+/*
+ * Sets up this frame_t to be scheduled for sending after the specified time
+ * has elapsed.  The frame_t will be queued and recycled automatically once it
+ * has been sent.
+ */
+void
+frame_schedule(tap_t *tap, fnode_t *n, const struct timeval *tv)
+{
+       fnode_t *fn;
+
+       assert(tap != NULL && n != NULL && tv != NULL);
+
+       if (!timerisset(&time_event) || timercmp(tv, &time_event, <))
+               time_event = *tv;
+       if ((fn = fnode_new(n->frame)) != NULL) {
+               fn->scheduled = *tv;
+               DLIST_APPEND(&tap->schedq, (node_t *)fn);
+       }
+}
+
+/*
+ * Attempts to create a new storage key.  Returns a pointer to a void * on
+ * success, or NULL on error.  This void * may then be initialized and used as
+ * wanted.
+ */
+void **
+key_create(const char *key)
+{
+       storage_t       *node;
+
+       assert(key != NULL);
+
+       if (hashtable_lookup(&storage_table, key, strlen(key)) != NULL)
+               return NULL;
+
+       if ((node = (storage_t *)pool_alloc(&key_pool, false)) != NULL) {
+
+               (void) strncpy(node->key, key, sizeof(node->key) - 1);
+               node->udata = NULL;
+
+               if (hashtable_link(&storage_table, (hashnode_t *)node,
+                   node->key, strlen(key), false))
+                       return &node->udata;
+
+               (void) pool_free((pnode_t *)node);
+       }
+
+       return NULL;
+}
+
+/*
+ * Attempts to locate an existing storage key.  Returns a pointer to a void *
+ * on success, or NULL if it doesn't exist.  This void * may previously have
+ * been initialized for custom storage.
+ */
+void **
+key_lookup(const char *key)
+{
+       storage_t       *node;
+
+       assert(key != NULL);
+
+       if ((node = (storage_t *)hashtable_lookup(&storage_table, key,
+           strlen(key))) != NULL)
+               return &node->udata;
+
+       return NULL;
+}
+
+/*
+ * Attempts to destroy an existing storage key.  Does nothing about the
+ * storage void *, it thus should be freed as necessary by the module.
+ * It is possible to not call this function and expect the storage key to
+ * still exist after modules were reloaded.
+ */
+void
+key_destroy(const char *key)
+{
+       hashnode_t      *node;
+
+       assert(key != NULL);
+
+       if ((node = hashtable_lookup(&storage_table, key, strlen(key)))
+           != NULL) {
+               hashtable_unlink(&storage_table, (node));
+               (void) pool_free((pnode_t *)node);
+       }
+}
+
+void
+warning(int err, const char *fmt, ...)
+{
+       va_list         lst;
+       char            msgbuf[1024];
+       sigset_t        set, oset;
+
+       (void) sigemptyset(&set);
+       (void) sigaddset(&set, SIGALRM);
+       (void) sigaddset(&set, SIGHUP);
+       oset = set;
+       (void) sigprocmask(SIG_BLOCK, &set, NULL);
+
+       /*
+        * Since we want to append the errno error message after the custom
+        * message, we need to store it in a buffer rather than simply use
+        * vsyslog(3).
+        */
+       va_start(lst, fmt);
+       (void) vsnprintf(msgbuf, sizeof(msgbuf) - 1, fmt, lst);
+       va_end(lst);
+
+       if (err != 0)
+               syslog(LOG_NOTICE, "warning: %s: %s", msgbuf, strerror(err));
+       else
+               syslog(LOG_NOTICE, "notice: %s", msgbuf);
+
+       (void) sigprocmask(SIG_UNBLOCK, &oset, NULL);
+}
diff --git a/mmsoftware/tap-bridge/tap-bridge.h b/mmsoftware/tap-bridge/tap-bridge.h
new file mode 100644 (file)
index 0000000..db68e76
--- /dev/null
@@ -0,0 +1,149 @@
+/* $Id: tap-bridge.h,v 1.1 2007/12/17 19:56:07 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2007, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+
+#include <mmlist.h>
+#include <mmpool.h>
+
+
+#define MTU            1500
+/*
+ * tap(4) provides us with the ethernet header but not the checksum
+ * Bytes 0-5 dstmac, 6-11 srcmac, 12-13 ethertype, and we add our header
+ * 14-15 32-bit padding bytes.
+ */
+#define FRAMESIZE      (MTU + 14 + 2)
+#define TIMER_HZ       10
+
+
+typedef uint64_t       macaddr_t;
+typedef struct tap     tap_t;
+typedef struct frame   frame_t;
+typedef struct fnode   fnode_t;
+typedef struct module  module_t;
+typedef struct modnode modnode_t;
+
+/*
+ * Represents a tap(4) interface.
+ */
+struct tap {
+       char            *name;
+       int             fd;
+       macaddr_t       mac;
+       list_t          sendq, schedq;
+};
+
+/*
+ * Holds an ethernet frame.  A reference count allows multiple fnode_t objects
+ * to represent this frame_t object.
+ */
+struct frame {
+       pnode_t         node;
+       macaddr_t       origin, destination;
+       struct timeval  received;
+       size_t          size;
+       uint16_t        type;
+       uint8_t         *data;
+       int             refcount;
+};
+
+/*
+ * Passed to module receive callback functions so that each may have its own
+ * copy it can queue or discard as it wishes.
+ */
+struct fnode {
+       pnode_t         node;
+       frame_t         *frame;
+       struct timeval  scheduled;
+};
+
+/*
+ * Every filter module must provide this structure, named "module".
+ * <init> must hold the module's initialization code and will be called at
+ *        module loading time.
+ * <recv> will we passed an fnode_t representing a frame_t for every received
+ *        frame from the tap(4) interface.  The module may defer any
+ *        processing by destroying the node and returning true.  It may drop
+ *        the frame_t by destroying the node and returning false.  It may
+ *        use frame_send() to send it on a tap_t.  It also may queue its
+ *        fnode_t as wanted via its pnode_t's node_t.  The underlaying frame_t
+ *        will only ever be destroyed when all fnode_t representing it are
+ *        destroyed.
+ * <tick> is optional and may be NULL.  If a function is provided it will get
+ *        called every tap-bridge scheduling round (at a frequency of
+ *        TIMER_HZ).
+ * <exit> invoked when the module is unloaded.  Should free any resources it
+ *        holds.
+ * XXX I should provide a means for module to be able to keep persistent
+ * resources.  For instance, the logging module may want to keep its opened
+ * fifo file open as it doesn't want the reader to receive an EOF.
+ * However, the only safe way to do this using a decent API might be to
+ * provide named keys looked up in a hash table for modules to use.
+ * If every module has a unique persistent identifier this could also be
+ * done...  we could use the module name as a key for instance, and provide a
+ * void ** to the init function which it may use however it wishes.
+ * If doing this, we perhaps also want to provide an application global shared
+ * pointer to modules as well which might be good for particular applications.
+ * A general purpose named keys system would allow them to do both...
+ */
+struct module {
+       bool            (*init)(tap_t *, tap_t *);
+       bool            (*recv_int)(tap_t *, fnode_t *);
+       bool            (*recv_ext)(tap_t *, fnode_t *);
+       void            (*tick)(struct timeval *);
+       void            (*exit)(void);
+};
+
+struct modnode {
+       pnode_t         node;
+       void            *handle;
+       module_t        *module;
+};
+
+extern frame_t         *frame_new(void);
+extern fnode_t         *fnode_new(frame_t *);
+extern void            fnode_destroy(fnode_t *);
+extern size_t          macaddr_string(char *, size_t, macaddr_t *);
+extern void            frame_send(tap_t *, fnode_t *);
+extern void            frame_schedule(tap_t *, fnode_t *,
+                           const struct timeval *);
+extern void            *time_setcallback(void (*)(const struct timeval *));
+extern void            *recv_setcallback(tap_t *,
+                           void (*)(tap_t *, frame_t *));
+extern void            **key_create(const char *);
+extern void            **key_lookup(const char *);
+extern void            key_destroy(const char *);
+extern void            warning(int, const char *, ...);
diff --git a/mmsoftware/tap-bridge/tap-headers.h b/mmsoftware/tap-bridge/tap-headers.h
new file mode 100644 (file)
index 0000000..8e342ac
--- /dev/null
@@ -0,0 +1,69 @@
+/* $Id: tap-headers.h,v 1.1 2007/12/17 19:56:07 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2007, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+
+/*
+ * ARP
+ */
+
+struct hdr_arp {
+       uint16_t        htype;
+       uint16_t        ptype;
+       uint8_t         hlen;
+       uint8_t         plen;
+       uint16_t        oper;
+};
+
+/* IPv4 -> MAC address resolution */
+struct hdr_arp_p4h {
+       struct hdr_arp  hdr;
+       uint8_t         sha[6];
+       uint32_t        spa;
+       uint8_t         tha[6];
+       uint32_t        tpa;
+} __attribute__((__packed__));
+
+/* MAC -> IPv4 address resolution */
+struct hdr_arp_hp4 {
+       struct hdr_arp  hdr;
+       uint32_t        sha;
+       uint8_t         spa[6];
+       uint32_t        tha;
+       uint8_t         tpa[6];
+} __attribute__((__packed__));
+
+