Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/glomsplit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* TX glom head split (AmiNetXDuo #89). Pure: no Amiga headers, so the host
* test in tests/test_glomsplit.c builds it with cc.
*
* In a glom of three or more frames whose first two are pure ACKs of one TCP
* flow, the head ACK was absent from the peer's capture (A11: 21 of 21); in
* two-frame gloms of such a pair it was not (A11 0 of 44, G2 0 of 54). Where
* it is lost after CMD53 is not established. The driver never sends such a
* glom: it ends the glom after the pair, so a same-flow ACK run goes in pairs.
*
* Pure ACK: IPv4 (not a fragment, options allowed) or IPv6 (TCP as the first
* next header), TCP with no payload, ACK set, SYN, FIN and RST clear. TCP
* options (timestamps, SACK) do not count as payload. Same flow: equal
* source and destination addresses and ports.
*/
#ifndef WIFIPI_GLOMSPLIT_H
#define WIFIPI_GLOMSPLIT_H

/* frame: from the Ethernet header, len bytes; *key and *keyLen give the
address pair, *ports the port pair */
static inline int wifipi_pure_ack(const unsigned char *f, unsigned long len,
const unsigned char **key, unsigned int *keyLen,
const unsigned char **ports)
{
const unsigned char *ip = f + 14, *tcp;
unsigned long hl, iplen, thl;
unsigned int type;

if (f == 0 || len < 14 + 20) return 0;
type = (f[12] << 8) | f[13];

if (type == 0x0800)
{
if ((ip[0] >> 4) != 4 || ip[9] != 6) return 0;
if ((ip[6] & 0x3f) | ip[7]) return 0; /* MF or offset */
hl = (ip[0] & 15) * 4;
iplen = (ip[2] << 8) | ip[3];
if (hl < 20) return 0;
*key = ip + 12; *keyLen = 8;
}
else if (type == 0x86dd)
{
if (len < 14 + 40 || (ip[0] >> 4) != 6 || ip[6] != 6) return 0;
hl = 40;
iplen = 40 + ((ip[4] << 8) | ip[5]);
*key = ip + 8; *keyLen = 32;
}
else return 0;

if (iplen < hl + 20 || 14 + iplen > len) return 0;
tcp = ip + hl;
thl = (tcp[12] >> 4) * 4;
if (thl < 20 || hl + thl != iplen) return 0; /* payload */
if ((tcp[13] & 0x17) != 0x10) return 0; /* ACK; no FIN/SYN/RST */
*ports = tcp;
return 1;
}

/* frames[0..1] and lens[0..1]: the head of a glom of count frames */
static inline int wifipi_glom_head_split(const unsigned char *const *frames,
const unsigned long *lens,
unsigned long count)
{
const unsigned char *k0, *k1, *p0, *p1;
unsigned int n0, n1, i;

if (count < 3) return 0;
if (!wifipi_pure_ack(frames[0], lens[0], &k0, &n0, &p0)) return 0;
if (!wifipi_pure_ack(frames[1], lens[1], &k1, &n1, &p1)) return 0;
if (n0 != n1) return 0;
for (i = 0; i < n0; i++) if (k0[i] != k1[i]) return 0;
for (i = 0; i < 4; i++) if (p0[i] != p1[i]) return 0;
return 1;
}

/* frames the next transfer takes from a queue of count */
static inline unsigned long wifipi_glom_take(const unsigned char *const *frames,
const unsigned long *lens,
unsigned long count)
{
return wifipi_glom_head_split(frames, lens, count) ? 2 : count;
}

#endif
33 changes: 32 additions & 1 deletion src/packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "wifipi.h"
#include "packet.h"
#include "brcm_wifi.h"
#include "glomsplit.h"
#include <aminetxduo/anxs2ext.h>

#ifndef PAD
Expand Down Expand Up @@ -1857,7 +1858,8 @@ void ProcessDataPacket(struct SDIO *sdio, UBYTE *packet, ULONG packetLength)
* Byte 6~7: Reserved
*/

int SendGlomDataPacket(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE count)
/* one transfer from the head of ioList; returns the frames it took */
static UBYTE SendGlomOnce(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE count)
{
struct WiFiBase *WiFiBase = sdio->s_WiFiBase;
struct ExecBase *SysBase = sdio->s_SysBase;
Expand All @@ -1867,6 +1869,8 @@ int SendGlomDataPacket(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE coun
struct PacketHeaderHW *pktBase = sdio->s_TXBuffer;
UBYTE *byteBuffer = sdio->s_TXBuffer;
struct GlomHeader *lastGh = NULL;
const UBYTE *head[2];
ULONG headLen[2];

for (UBYTE i = 0; i < count; i++)
{
Expand Down Expand Up @@ -1915,6 +1919,12 @@ int SendGlomDataPacket(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE coun
*ptr++ = 0;
*ptr++ = 0;

if (i < 2)
{
head[i] = ptr;
headLen[i] = packetLength - sizeof(struct Packet) - sizeof(struct GlomHeader) - 4;
}

if ((io->ios2_Req.io_Flags & SANA2IOF_RAW) == 0)
{
// Copy destination
Expand Down Expand Up @@ -1956,6 +1966,14 @@ int SendGlomDataPacket(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE coun
if (packetLength % 16 != 0) bug("\n");
}
#endif
/* two pure ACKs of one flow never head 3+: end the glom here, as
wifipi_glom_take() says (#89) */
if (i == 1 && wifipi_glom_head_split(head, headLen, count))
{
gh->gh_LastItem = 1;
count = 2;
}

// Increase total length by packet length (aligned)
totalLength += (packetLength + 3) & ~3;
}
Expand Down Expand Up @@ -1998,6 +2016,19 @@ int SendGlomDataPacket(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE coun
unit->wu_Stats.PacketsSent++;
}

return count;
}

int SendGlomDataPacket(struct SDIO *sdio, struct IOSana2Req **ioList, UBYTE count)
{
while (count)
{
UBYTE n = SendGlomOnce(sdio, ioList, count);

ioList += n;
count -= n;
}

return 1;
}

Expand Down
Loading