[Gd-chatter] r10960 - in trunk/libraries: layer packetizer packetizer/packetizer-test protocols
hannes at gwydiondylan.org
hannes at gwydiondylan.org
Thu Nov 16 23:35:54 CET 2006
Author: hannes
Date: Thu Nov 16 23:35:51 2006
New Revision: 10960
Modified:
trunk/libraries/layer/layer.dylan
trunk/libraries/packetizer/packetizer-test/packetizer-test.dylan
trunk/libraries/packetizer/stretchy-byte-vector.dylan
trunk/libraries/protocols/ipv4.dylan
Log:
Bug: 7299
packetizer:
* fixed encode-integer for the case the subsequence ends at a byte boundary, and has the full last byte
* added test case dns-foo-assemble
protocols:
* fixed calculate-checksum that it no longer overflows (big integers would be really nice)
layer:
* implemented ipv4 reassembly and fragmentation
Modified: trunk/libraries/layer/layer.dylan
==============================================================================
--- trunk/libraries/layer/layer.dylan (original)
+++ trunk/libraries/layer/layer.dylan Thu Nov 16 23:35:51 2006
@@ -211,25 +211,105 @@
let ipv4-fan-in = make(<fan-in>);
connect(ip-socket.decapsulator, ipv4-fan-in);
connect(ip-broadcast-socket.decapsulator, ipv4-fan-in);
- connect(ipv4-fan-in, ip-over-ethernet.ip-layer.demultiplexer);
+ connect(ipv4-fan-in, ip-over-ethernet.ip-layer.reassembler);
register-adapter(ip-over-ethernet.ip-layer, ip-over-ethernet);
ip-over-ethernet.ip-layer.default-ip-address := ip-over-ethernet.v4-address;
end;
+define open generic fragmented-packets (object :: <ip-reassembler>) => (res :: <vector-table>);
+
+define class <ip-reassembler> (<filter>)
+ constant slot fragmented-packets :: <vector-table> = make(<vector-table>);
+end;
+
+define inline function generate-assembly-id (ip-frame :: <ipv4-frame>)
+ => (res :: <vector>)
+ concatenate(ip-frame.source-address.data,
+ ip-frame.destination-address.data,
+ assemble-frame-as(<2byte-big-endian-unsigned-integer>, ip-frame.identification));
+end;
+
+
+define generic first-packet (obj :: <frag-packet>) => (res);
+define generic payloads (obj :: <frag-packet>) => (res :: <stretchy-vector>);
+define generic next-fragment-offset (obj :: <frag-packet>) => (res :: <integer>);
+define generic next-fragment-offset-setter (value :: <integer>, obj :: <frag-packet>) => (res :: <integer>);
+define generic timeout (obj :: <frag-packet>) => (res);
+define generic timeout-setter (value, obj :: <frag-packet>) => (res);
+
+define class <frag-packet> (<object>)
+ constant slot first-packet, required-init-keyword: first-packet:;
+ constant slot payloads :: <stretchy-vector> = make(<stretchy-vector>);
+ slot next-fragment-offset :: <integer> = 0, init-keyword: next-offset:;
+ slot timeout, required-init-keyword: timeout:;
+end;
+
+//XXX: this really should take care of out-of-order segments
+// and don't set the precondition that IP fragments arrive
+// in correct order
+define method push-data-aux (input :: <push-input>,
+ node :: <ip-reassembler>,
+ frame :: <ipv4-frame>)
+ if (frame.fragment-offset = 0)
+ if (frame.more-fragments = 0)
+ //fast path, just pass frame
+ push-data(node.the-output, frame);
+ else
+ let packet-id = generate-assembly-id(frame);
+ let timer = make(<timer>, in: 300, event: curry(remove-key!, node.fragmented-packets, packet-id));
+ node.fragmented-packets[packet-id]
+ := make(<frag-packet>,
+ first-packet: frame,
+ timeout: timer,
+ next-offset: byte-offset(byte-offset(frame-size(frame.payload))));
+ end;
+ else
+ let packet-id = generate-assembly-id(frame);
+ let frag-packet = element(node.fragmented-packets, packet-id, default: #f);
+ if (frag-packet)
+ if (frag-packet.next-fragment-offset = frame.fragment-offset)
+ add!(frag-packet.payloads, frame.payload.packet);
+ frag-packet.next-fragment-offset
+ := frag-packet.next-fragment-offset + byte-offset(byte-offset(frame-size(frame.payload)));
+ cancel(frag-packet.timeout);
+ frag-packet.timeout
+ := make(<timer>, in: 300, event: curry(remove-key!, node.fragmented-packets, packet-id));
+ else
+ format-out("Received out of order IP Fragment (%d, expected %d)\n",
+ frame.fragment-offset, frag-packet.next-fragment-offset);
+ end;
+ if (frame.more-fragments = 0)
+ let fp = frag-packet.first-packet;
+ fp.cache.payload := parse-frame(fp.payload-type,
+ reduce(concatenate, fp.payload.packet, frag-packet.payloads),
+ parent: fp);
+ push-data(node.the-output, fp);
+ cancel(frag-packet.timeout);
+ remove-key!(node.fragmented-packets, packet-id);
+ end;
+ else
+ format-out("Received out of order IP Fragment (offset %d, but didn't receive the first yet)\n",
+ frame.fragment-offset);
+ end;
+ end;
+end;
+
define open generic send-socket (object :: <object>) => (res);
define open generic send-socket-setter (value :: <object>, object :: <object>) => (res);
define generic default-ip-address (object :: <layer>) => (res :: <ipv4-address>);
define generic default-ip-address-setter (value :: <ipv4-address>, object :: <layer>) => (res :: <ipv4-address>);
define open generic adapters (object :: <ip-layer>) => (res);
define open generic routes (object :: <ip-layer>) => (res);
+define open generic reassembler (object :: <ip-layer>) => (res);
define class <ip-layer> (<layer>)
slot send-socket :: type-union(<socket>, <adapter>);
constant slot adapters = make(<stretchy-vector>);
slot default-ip-address :: <ipv4-address>;
constant slot routes = make(<stretchy-vector>);
+ constant slot reassembler = make(<ip-reassembler>);
end;
define class <route> (<object>)
@@ -252,16 +332,45 @@
sort!(ip.routes, test: method(x, y) x.cidr.cidr-netmask > y.cidr.cidr-netmask end)
end;
+//implicit assumptions made: *frame has no ip-options (header-length = 5)
define method initialize (ip-layer :: <ip-layer>,
#rest rest, #key, #all-keys);
let cls = make(<closure-node>,
closure: method(x)
let (adapter, next-hop)
= find-adapter-for-forwarding(ip-layer, x.destination-address);
- send(adapter, next-hop, x)
+ let mtu = find-mtu-for-destination(adapter, x.destination-address) * 8;
+ let full-payload = assemble-frame(x.payload).packet;
+ let data-size = frame-size(x.payload);
+ if (mtu < data-size)
+ x.more-fragments := 1;
+ for (i from 0 below data-size - mtu by mtu,
+ j from 0)
+ x.payload := parse-frame(<raw-frame>, subsequence(full-payload, start: i, length: mtu));
+ let ip-frame = assemble-frame(x);
+ fixup!(ip-frame);
+ send(adapter, next-hop, ip-frame);
+ x.fragment-offset := byte-offset(byte-offset((j + 1) * mtu));
+ end;
+ end;
+ x.more-fragments := 0;
+ x.payload := parse-frame(<raw-frame>, subsequence(full-payload,
+ start: x.fragment-offset * 8 * 8,
+ length: modulo(data-size, mtu)));
+ x.total-length := #f;
+ let ip-frame = assemble-frame(x);
+ fixup!(ip-frame);
+ send(adapter, next-hop, ip-frame);
end);
connect(ip-layer.fan-in, cls);
+ connect(ip-layer.reassembler, ip-layer.demultiplexer);
end;
+
+define method find-mtu-for-destination (adapter :: <ip-over-ethernet-adapter>, destination :: <ipv4-address>)
+ => (res :: <integer>)
+ 1480;
+end;
+
define method register-adapter (ip :: <ip-layer>,
adapter :: <ip-over-ethernet-adapter>)
add!(ip.adapters, adapter);
@@ -504,40 +613,39 @@
*/
let ip-layer = make(<ip-layer>);
register-route(ip-layer, make(<next-hop-route>, cidr: as(<cidr>, "0.0.0.0/0"),
- next-hop: ipv4-address("192.168.0.1")));
+ next-hop: ipv4-address("192.168.2.1")));
let ip-over-ethernet = make(<ip-over-ethernet-adapter>,
ethernet: ethernet-layer,
arp: arp-handler,
ip-layer: ip-layer,
- ipv4-address: ipv4-address("192.168.0.24"),
+ ipv4-address: ipv4-address("192.168.2.23"),
netmask: 24);
let icmp-handler = make(<icmp-handler>);
let icmp-over-ip = make(<icmp-over-ip-adapter>,
ip-layer: ip-layer,
icmp-handler: icmp-handler);
let thr = make(<thread>, function: curry(toplevel, int));
-/*
send(icmp-handler.ip-socket,
ipv4-address("213.73.91.29"),
make(<icmp-frame>,
- type: 8,
+ icmp-type: 8,
code: 0,
payload: parse-frame(<raw-frame>, as(<byte-vector>, #(#x23, #x42, #x0, #x0)))));
send(icmp-handler.ip-socket,
ipv4-address("212.202.174.224"),
make(<icmp-frame>,
- type: 8,
+ icmp-type: 8,
code: 0,
payload: parse-frame(<raw-frame>, as(<byte-vector>, #(#x23, #x42, #x0, #x0)))));
send(icmp-handler.ip-socket,
ipv4-address("192.168.0.1"),
make(<icmp-frame>,
- type: 8,
+ icmp-type: 8,
code: 0,
payload: parse-frame(<raw-frame>, as(<byte-vector>, #(#x23, #x42, #x0, #x0)))));
- format-out("Mac 192.168.2.1: %=\n", element(arp-handler.arp-table, ipv4-address("192.168.2.1"), default: #f));
-*/
+// format-out("Mac 192.168.2.1: %=\n", element(arp-handler.arp-table, ipv4-address("192.168.2.1"), default: #f));
+
ip-layer;
end;
Modified: trunk/libraries/packetizer/packetizer-test/packetizer-test.dylan
==============================================================================
--- trunk/libraries/packetizer/packetizer-test/packetizer-test.dylan (original)
+++ trunk/libraries/packetizer/packetizer-test/packetizer-test.dylan Thu Nov 16 23:35:51 2006
@@ -385,8 +385,8 @@
end;
define protocol dns-foo (container-frame)
- field typed :: <2bit-unsigned-integer>;
- field pointer :: <14bit-unsigned-integer>;
+ field typed :: <2bit-unsigned-integer> = 3;
+ field pointer :: <14bit-unsigned-integer> = 42;
end;
define test dns-foo-parsing ()
@@ -394,6 +394,14 @@
check-equal("type of frame is correct", 3, frame.typed);
check-equal("pointer of frame is correct", #x4e, frame.pointer);
end;
+
+define test dns-foo-assemble ()
+ let f = make(<dns-foo>);
+ let as = assemble-frame(f);
+ check-equal("assembling of dns-foo[0] is correct", 192, as.packet[0]);
+ check-equal("assembling of dns-foo[1] is correct", 42, as.packet[1]);
+end;
+
define suite packetizer-suite ()
test packetizer-parser;
test packetizer-dynamic-parser;
@@ -426,6 +434,7 @@
test half-byte-modify;
test half-bytes-assembling;
test bits-assemble;
+ test dns-foo-assemble;
end;
begin
Modified: trunk/libraries/packetizer/stretchy-byte-vector.dylan
==============================================================================
--- trunk/libraries/packetizer/stretchy-byte-vector.dylan (original)
+++ trunk/libraries/packetizer/stretchy-byte-vector.dylan Thu Nov 16 23:35:51 2006
@@ -455,7 +455,9 @@
seq.real-data[first-byte + i] := logand(#xff, ash(value, - (count - i * 8 + seq.bit-start-index)));
end;
//last element
- if ((bits > 0) & (fullbytes >= 0))
+ if ((seq.bit-end-index = 8) & (fullbytes > 0))
+ seq.real-data[first-byte + fullbytes] := logand(#xff, value);
+ elseif ((bits > 0) & (fullbytes >= 0))
seq.real-data[first-byte + fullbytes + 1]
:= logior(logand(seq.real-data[first-byte + fullbytes + 1],
ash(#xff, - bits)),
Modified: trunk/libraries/protocols/ipv4.dylan
==============================================================================
--- trunk/libraries/protocols/ipv4.dylan (original)
+++ trunk/libraries/protocols/ipv4.dylan Thu Nov 16 23:35:51 2006
@@ -84,17 +84,16 @@
define function calculate-checksum (frame :: <byte-sequence>,
count :: <integer>) => (res :: <integer>)
- let checksum :: <integer> = 0;
+ let checksum = 0.0d0;
for (i from 0 below count - 1 by 2)
checksum := checksum + ash(frame[i], 8) + frame[i + 1];
end;
if (logand(#x1, count) = 1)
checksum := checksum + ash(frame[count - 1], 8);
end;
- while (checksum > (2 ^ 16 - 1))
- checksum := ash(checksum, -16) + logand(#xffff, checksum);
- end;
- logand(#xffff, lognot(checksum));
+ let (low, high) = floor/(checksum, 2 ^ 16);
+ let res :: <integer> = round(high) + low;
+ logand(#xffff, lognot(res));
end;
define method fixup! (frame :: <unparsed-ipv4-frame>,
More information about the chatter
mailing list