view · edit · attach · print · history

Wireless Multi-Boot Host Association

Here’s basically how it works:

The Nintendo DS in WMB host mode (eg. starting a multiplayer game from Mario64DS) sits there and broadcasts 802.11 beacons. These are standard beacons, except with no SSID (as opposed to a hidden or null SSID, which is an SSID tagged-attribute with 0 length. I don’t know if this is standard, but it breaks most assumptions right off the bat. ^_^)

1. Beacons

1.1 802.11 frame

The 802.11 header details are: (00:09:bf:xx:yy:zz is the Host)

Type 0×80 (Type Management, subtype Beacon)
Duration 0
Destination ff:ff:ff:ff:ff:ff
Src 00:09:bf:xx:yy:zz
BSS 00:09:bf:xx:yy:zz
Fragment 0, Sequence increments normally

802.11 Management frame header:

Timestamp (irrelevant. 0′d out works fine)
Interval 0xce (I’ve seen 0xce (0.210944 seconds)and also 00d0 (0.212922) from the same cart.)
Capabilities 0021 (AP ‘ESS’, short preamble)

Tagged attributes:

Supported rates - Tag 1, length 2
* 0×82, 0×84 (1Mb/s and 2Mb/s are mandatory)
DS Params - Tag 3, length 1 (This is the channel the data will flow on)
* 0×0d (Channel 13. You really ought to pull this from the driver you’re talking to.)
TIM - Tag 5, length 5 (Traffic Information Map. I dunno what this _is_)
* 0/1 2 0 0000 (The first value cycles, the second is the period of the cycle)
Vendor - Tag 221, length 24 or 136. This starts with 0×00 0×09 0xbf, Nintendo’s OID

so we know who’s Vendor tag this is.

* See the rest of this document. ^_^

Then comes the DS Data. I currently break this down into three parts:

  • Beacon Header (21 bytes)
  • Game Header
  • Payload

1.2 Beacon Header

The Beacon Header is largely a mystery to me. The only meaningful value I have is the payload length, 8 bits at offset 0×0f. It’s either 0 for an empty beacon (Mario64DS starts with three of these) or 112 for a dataful beacon. There are two other values that vary with datafulness, offset 0×07 and offset 0×10.

The below is the current table from my perl script, dumpinfo.pl, which extracts them. I’ve also looked at a ridgeracer dump I got a hold of, but not bothered analysing further yet.

Input for dumpinfo.pl is just a binary dump of the beacon header onwards. You can either get them out of ethereal (although you have to strip off the 00,09,bf at the start) or with capread.c, documented at Wireless Multi Boot Data

It’s Offset => [“Name”,bytes], BTW.

    # The /’d values are former for empty frame (repeated three times on Mario 64 DS)
    # latter for packetful frame (112 bytes payload) cycled adnauseum on Mario 64 DS)

my %meanings = (

    0 => [“\tUnknown 0″, 1],
    1 => [“\tUnknown 10″, 1],
    2 => [“\tUnknown 0″, 1],
    3 => [“\tUnknown 0″, 1],
    4 => [“\tUnknown 0″, 1],
    5 => [“\tUnknown 1″, 1],
    6 => [“\tUnknown 0″, 1],
    7 => [“\tUnknown 0/64″, 1],
    8 => [“\tUnknown 0″, 1],
    9 => [“\tUnknown 17″, 1], # 15 in Ridge Racer DS from firefly
    10 => [“\tUnknown 0″, 1],
    11 => [“\tUnknown 64″, 1],
    12 => [“\tUnknown 0″, 1],
    13 => [“\tUnknown 86″, 1], # 218 in Ridge Racer DS from firefly
    14 => [“\tUnknown 220″, 1], # 7 in Ridge Racer DS from firefly
    15 => [“Payload Length”, 1], # Firefly’s WMB 0′s this…
    16 => [“\tUnknown 9/11″, 1],
    17 => [“\tUnknown 254″, 1], # 0 in Ridge Racer DS from firefly
    18 => [“\tUnknown 1″, 1],
    19 => [“\tUnknown 8″, 1],
    10 => [“\tUnknown 0″, 1],
    );

1.3  Game Header

The Game Header is also largely a mystery to me, but less so. It looks like this in perl:

my %payloadhdr = (

    # 1100 4000 0000 0000 43e2 0009 6200
    # 1100 4000 0000 0100 6eab 0109 6200
    # 1100 4000 0000 0200 f385 0209 6200
    # 1100 4000 0000 0300 969b 0309 6200
    # 1100 4000 0000 0400 8aa1 0409 6200
    # 1100 4000 0000 0500 cf96 0509 6200
    # 1100 4000 0000 0600 f0bb 0609 6200
    # 1100 4000 0000 0700 e7d1 0709 6200
    # 1100 4000 0000 0800 f6af 0809 4800

    # 1100 4000 0200 0900 f6fd 0109 0100 (One byte: 0×01) — Different type?

    0 => [“\tUnknown 17″, 1], # 24 in Ridge Racer DS from firefly
    1 => [“\tUnknown 0″, 1], # 5 in Ridge Racer DS from firefly
    #http://forum.gbadev.org/viewtopic.php?p=32294#32294
    2 => [“\tUnknown 0×0040″, 2], # 0×0016 in Ridge Racer DS from firefly
    # This one’s 2 when the packet is empty…
    4 => [“\tCmd? 0/2″, 1],
    5 => [“\tUnknown 0″, 1], # 2 in Ridge Racer DS from firefly
    # Repeats when the whole sequence repeats
    6 => [“\tUnknown 0″, 1],
    7 => [“Loop sequence”, 1],
    8 => [“Checksum”, 2],
    10 => [“Beacon part #”, 1],
    11 => [“\tUnknown 9″, 1],
    # More of a position…
    12 => [“Part Length”, 1],
    13 => [“\tUnknown 0″, 1],

);

The comments above the structure show Mario64 DS’s 10-period cycle of entries. The Loop Sequence cycles with the period, while the Beacon part # cycles with dataful packets only, and is 1 with the final packet. The Part Length is the amount of the actual payload in this beacon, and even when it is less than 98 (the maximum) the packet is padded out with 0′s.

The checksum calculation is available on the gbadev forum, thanks to Gladius/crazyc. Here’s it in perl:

sub gladiuschecksum {

    my $input = shift;
    my $sum = 0;
    my $offset = 0;
    do {
        $sum += unpack(“S”, substr $input, $offset, 2);
        $offset += 2;
    } while $offset < length $input;

    my $result = ((($sum & 0xffff) + ($sum >> 16) + 1) * −1) & 0xffff;
    return $result;

}

and in C: u_int32_t nds_checksum(u_int16_t *buff, int length) { /* Taken from gladius’s code, whacky pointer stuff removed. */

    int j;
    u_int32_t crc = 0;
    for (j = 0; j < length; j++) {
        crc +=  buff[j];
    }
    crc = (((crc >> 16) + (crc & 0xffff) + 1) * −1) & 0xffff;
    return crc;

}

The checksum is calculated with the last four bytes of the Game Header, and then payload length bytes of the payload. (IE it doesn’t include the zero padding).

NOTE! The above two functions take their count in 16-bit words, not bytes. So you have to divide your length by two to get the right checksum.

1.4 Beacon Payload

As you can see, my payload is 8*98 + 72 = 856 bytes.

It’s contents almost all comes from the “Icon+titles (ROM)” (“ICON”) area, pointed at by 0×68 of the .nds file’s header. (Obviously, you can put whatever you want here, but for the case of WMBing an .nds you can fetch it from the .nds itself.)

It’s:

32 bytes from ICON + 0×220 (Palette)
512 bytes from ICON + 0×20 (Icon data)
00 05 ??? Maybe nickname len, but that’s daft.
Nickname (10 UCS-2 characters (20 bytes))
02 (Players allowed in game, will take 0xff but display only the last two digits)
00 ???
0×238 is first line of text (96 bytes)
0×298 is the rest of the text (96 bytes)

Where the text is the description that lives at ICON + 0×240,0×340,0×440,0×540,0×640 or 0×740 depending on it you want Japanese, English, French, German, Italian or Spanish. The text in the .nds file itself is all one block, seperated by 0×000a (newline) and that’s a handy place to split for the above.

This will be enough to get your game to appear on the screen, and is the easiest part of this process.

2.  Authentication and association

When you select and OK a game, the NintendoDS sends you a nice standard 14-byte 802.11 AUTH1 packet: (Short preamble. ^_^)

b0 10 a2 00 00 09 bf 91 ec 56 00 09 bf 91 ec 55 00 09 bf 91 ec 56 00 00 00 00 01 00 00 00

This is: (00:09:bf:gg:hh:ii is your client)

Type 0×10B0 (Type Management, subtype AUTH, power management flag)
Duration 0xa2
Destination 00:09:bf:xx:yy:zz
Src 00:09:bf:gg:hh:ii
BSS 00:09:bf:xx:yy:zz
Fragment 0, Sequence increments

802.11 Management frame header:

Auth Type Open (0×0000)
Auth Seq 1 (0×0001)
Auth Status OK (0×0000)

You ACK it, and send back an ATUH2

Type 0×00B0 (Type Management, subtype AUTH)
Duration 0xa2
Destination 00:09:bf:gg:hh:ii
Src 00:09:bf:xx:yy:zz
BSS 00:09:bf:xx:yy:zz
Fragment 0, Sequence increments

802.11 Management frame header:

Auth Type Open (0×0000)
Auth Seq 2 (0×0001)
Auth Status OK (0×0000)

The Nintendo DS replies with an ASSOC REQ: (66 bytes on the wire, don’t have the contents handy)

And you ACK and then reply to that with an ASSOC RESP:

Type 0×00? (Type Management, subtype ASSOC RESP)
Duration 0xa2
Destination 00:09:bf:gg:hh:ii
Src 00:09:bf:xx:yy:zz
BSS 00:09:bf:xx:yy:zz
Fragment 0, Sequence increments

802.11 Management frame header:

Capabilities 0021 (AP ‘ESS’, short preamble)
Assoc Status OK (0×0000)
Assoc ID 1 (0×0001)

Tagged attributes:

Supported rates - Tag 1, length 2
* 0×82, 0×84 (1Mb/s and 2Mb/s are mandatory)

Which you send, the DS acks, and you’re associated.

Now wait four seconds, and the DS will disassoc, which shows you were working.

See Wireless Multi Boot Data for what I currently know about the next stage. ^_^

3. Proof of concept code

[+This is old. I got it working on an rt2500, the speed setting on the prism54 refuses to affect tx-in-monitor… With luck the newer prism54 driver work will eventually bring up something that can be used+]

Firstly, a script to run after you’ve loaded your card’s driver: setup.sh It’s probably specific to the prism54, and quite simple. I just didn’t want to forget any steps in the process.

However, it’s not quite working. I turned on AVS support in my other prism54 card

iwpriv wlan0 set_prismhdr 1

and I’m sending at 1Mb/s according to it, while the NintendoDS is talking at 2Mb/s. This is despite the setup.sh happily reporting (with a little bit of kernel debugging) that it’s locking the card to speed 0×84 (2Mb/s.) >_<

Anyway, here’s the current cut of my C code to WMB a NintendoDS from a linux box. It is tested with a prism54 hardmac, although I see no reason it won’t work with a softmac using the -jb driver, or with any other card that aireplay claims to support.

It currently can convince the DS to associate with it, but the DS doesn’t ack the first data frame (I haven’t talked about this yet…) and four seconds later deassociates again.

For this code, I started with the socketraw function from aireplay.c, and then copied code from my earlier in-kernel version of this code built upon the prism54-jb softmac driver.

I must say that madwifi proved invaluable for this effort, since it provides a handy set of structures and defines so I don’t need to memorise all the magic numbers above. ^_^

test1.c

view · edit · attach · print · history
Page last modified on December 17, 2005, at 12:47 PM