Files
evan fe35cb68d6 feat(xteink): add ESP32-C3 Wi-Fi emulation with qemu AP
Ports the ESP32 Wi-Fi emulation (esp32c3_wifi, esp32_wifi_ap, esp32_wlan_*)
from the lcgamboa/PICSimLab QEMU fork and integrates it behind the
esp32c3_wifi NIC model, plus the supporting radio peripherals
(ANA/PHYA/FE/PWR manager).

- Exposes an open SSID 'qemu' bridged to the host via QEMU user-mode NAT
  (libslirp, now enabled in the native build).
- Adds ESP-IDF 5.5 compatibility fixes: channel resolution from the
  beacon DS Parameter Set IE / AP lookup, persistent SYSCON register
  backing (phy_module_has_clock_bits), DMA inlink/outlink descriptor
  mirroring, RX descriptor ring reset on item.next==0, and descriptor
  ownership/length semantics.
- xteink-emu now starts with -nic user,model=esp32c3_wifi and synthesizes
  an eFuse image with a fixed guest MAC.

Verified against a stock Arduino-ESP32 sketch: scans, finds 'qemu',
associates, gets DHCP 192.168.4.15, and completes an outbound TCP
connection. Unmodified CrossPoint firmware still needs the power-button
hold during boot (tracked separately).
2026-07-24 16:21:12 -04:00

788 lines
31 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* QEMU WLAN access point emulation
*
* Copyright (c) 2008 Clemens Kolbitsch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Modifications:
* 2008-February-24 Clemens Kolbitsch :
* New implementation based on ne2000.c
* 18/01/22 Martin Johnson : Modified for esp32 wifi emulation
* 08/12/23 lcgamboa@yahoo.com : Modified for esp32c3, espnow and SoftAp wifi emulation
*/
#include "qemu/osdep.h"
#include "net/net.h"
#include "qemu/timer.h"
#include "hw/irq.h"
#include "hw/misc/esp32_wifi.h"
#include "hw/misc/esp32_phya.h"
#include "esp32_wlan.h"
#include "esp32_wlan_packet.h"
// 50ms between beacons
#define BEACON_TIME 500000000
#define INTER_FRAME_TIME 5000000
#define WAIT_ACK_TIMEOUT 10000000
#define DEBUG 0
#define ENABLE_BEACON 1
#define DEBUG_DUMPFRAMES 0
#define DEBUG_WIRESHARK_IMPORT 0
// color defines
#define BLACK 0
#define RED 1
#define GREEN 2
#define YELLOW 3
#define BLUE 4
#define MAGNETA 5
#define CYAN 6
#define WHITE 7
#define ANSI_DEFAULT() printf("\033[0m")
#define ANSI_COLOR(f, b) printf("\033[%d;%dm", (f) + 30, (b) + 40)
#define ANSI_FG_LCOLOR(f) printf("\033[0;%dm", (f) + 30)
#define ANSI_FG_HCOLOR(f) printf("\033[1;%dm", (f) + 30)
access_point_info access_points[] = {
{"qemu", 1, -25, {0x10, 0x01, 0x00, 0xc4, 0x0a, 0x51}},
};
int nb_aps=sizeof(access_points)/sizeof(access_point_info);
static void Esp32_WLAN_beacon_timer(void *opaque)
{
struct mac80211_frame *frame;
Esp32WifiState *s = (Esp32WifiState *)opaque;
// only send a beacon if we are an access point
if((ENABLE_BEACON)&&(s->mode == Esp32_Mode_Station)){
if(s->ap_state!=Esp32_WLAN__STATE_STA_ASSOCIATED) {
for(int i=0;i<nb_aps;i++){
int ap = (i + s->beacon_ap)%nb_aps;
if (access_points[ap].channel==esp32_wifi_channel) {
memcpy(s->ap_macaddr,access_points[ap].mac_address,6);
frame = Esp32_WLAN_create_beacon_frame(&access_points[ap]);
Esp32_WLAN_init_ap_frame(s, frame);
Esp32_WLAN_insert_frame(s, frame);
break;
}
}
s->beacon_ap=(s->beacon_ap+1)%nb_aps;
}
}
timer_mod(s->beacon_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + BEACON_TIME);
}
static void Esp32_WLAN_inject_timer(void *opaque)
{
Esp32WifiState *s = (Esp32WifiState *)opaque;
struct mac80211_frame *frame;
frame = s->inject_queue;
if (frame) {
// remove from queue
s->inject_queue_size--;
s->inject_queue = frame->next_frame;
Esp32_sendFrame(s, (void *)frame, frame->frame_length,frame->signal_strength);
free(frame);
}
if (s->inject_queue_size > 0) {
// there are more packets... schedule
// the timer for sending them as well
timer_mod(s->inject_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + INTER_FRAME_TIME);
} else {
// we wait until a new packet schedules
// us again
s->inject_timer_running = 0;
}
}
static void Esp32_WLAN_Wait_ACk_timer(void *opaque)
{
Esp32WifiState *s = (Esp32WifiState *)opaque;
Esp32_WLAN_frame_delivered(s);
}
static void macprint(const uint8_t *p, const char * name) {
printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n",name, p[0],p[1],p[2],p[3],p[4],p[5]);
}
static void buffer_print(uint8_t * b, unsigned size){
for(int i=0;i < size ;i++) {
if((i%16)==0) printf("%04x: ",i);
printf("%02x ",b[i]);
if((i%16)==15) printf("\n");
}
if((size%16)!=0)printf("\n");
}
static void infoprint(struct mac80211_frame *frame) {
if(DEBUG_DUMPFRAMES) {
#if (DEBUG_WIRESHARK_IMPORT == 0 )
printf("\nFrame Info type:%d subtype:%d flags:%d duration:%d length:%d\n",frame->frame_control.type,
frame->frame_control.sub_type,frame->frame_control.flags,frame->duration_id, frame->frame_length);
printf("protocol_version: %i\n",frame->frame_control.protocol_version);
printf("type: %i ",frame->frame_control.type );
switch (frame->frame_control.type)
{
case IEEE80211_TYPE_MGT:
printf("MGT\n");
break;
case IEEE80211_TYPE_CTL:
printf("CTL\n");
break;
case IEEE80211_TYPE_DATA:
printf("DATA\n");
break;
}
printf("subtype: %i ",frame->frame_control.sub_type);
switch (frame->frame_control.type)
{
case IEEE80211_TYPE_MGT:
switch (frame->frame_control.sub_type)
{
case IEEE80211_TYPE_MGT_SUBTYPE_BEACON:
printf("BEACON\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_ACTION:
printf("ACTION\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_REQ:
printf("PROBE_REQ\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_RESP:
printf("PROBE_RESP\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION:
printf("AUTHENTICATION\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_DEAUTHENTICATION:
printf("DEAUTHENTICATION\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_REQ:
printf("ASSOCIATION_REQ\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_RESP:
printf("ASSOCIATION_RESP\n");
break;
case IEEE80211_TYPE_MGT_SUBTYPE_DISASSOCIATION:
printf("DISASSOCIATION\n");
break;
default:
printf("---\n");
break;
}
break;
case IEEE80211_TYPE_CTL:
switch (frame->frame_control.sub_type)
{
case IEEE80211_TYPE_CTL_SUBTYPE_ACK:
printf("ACK\n");
break;
default:
printf("---\n");
break;
}
break;
case IEEE80211_TYPE_DATA:
switch (frame->frame_control.sub_type)
{
case IEEE80211_TYPE_DATA_SUBTYPE_DATA:
printf("DATA\n");
break;
default:
printf("---\n");
break;
}
break;
}
printf("flags: %i\n",frame->frame_control.flags);
printf("duration_id: %i\n",frame->duration_id);
macprint(frame->destination_address,"destination");
macprint(frame->source_address,"source");
macprint(frame->bssid_address,"bssid");
printf("fragment_number: %u\n",frame->sequence_control.fragment_number);
printf("sequence_number : %u\n",frame->sequence_control.sequence_number);
switch (frame->frame_control.type)
{
case IEEE80211_TYPE_MGT:
switch (frame->frame_control.sub_type)
{
case IEEE80211_TYPE_MGT_SUBTYPE_BEACON:
printf("beacon timestamp : %lu\n",(unsigned long) frame->beacon_info.timestamp);
printf("beacon interval : %u\n",frame->beacon_info.interval);
printf("beacon capability : %u\n",frame->beacon_info.capability);
break;
default:
if(frame->frame_length > IEEE80211_HEADER_SIZE)
buffer_print((uint8_t *)frame->data_and_fcs, frame->frame_length - IEEE80211_HEADER_SIZE);
break;
}
break;
case IEEE80211_TYPE_CTL:
switch (frame->frame_control.sub_type)
{
default:
if(frame->frame_length > IEEE80211_HEADER_SIZE)
buffer_print((uint8_t *)frame->data_and_fcs, frame->frame_length - IEEE80211_HEADER_SIZE);
break;
}
break;
case IEEE80211_TYPE_DATA:
switch (frame->frame_control.sub_type)
{
default:
printf("EtherType 0x%02X%02X\n",frame->data_and_fcs[6],frame->data_and_fcs[7]);
if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==0x06) {
printf("ARP\n");
arp_header_t *header=(arp_header_t *)&frame->data_and_fcs[8];
printf("Operation %i\n", header->operation>>8);
}
if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==0x00) {
printf("IPV4\n");
ip_header_t *header=(ip_header_t *)&frame->data_and_fcs[8];
printf("Protocol: 0x%04X\n",header->protocol);
switch (header->protocol)
{
case 0x11:
printf("UDP\n");
break;
case 0x06:
printf("TCP\n");
break;
}
}
if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==0xdd) {
printf("IPV6\n");
}
if(frame->frame_length > IEEE80211_HEADER_SIZE)
buffer_print((uint8_t *)frame->data_and_fcs, frame->frame_length - IEEE80211_HEADER_SIZE);
break;
}
break;
}
printf("frame_length : %u\n", frame->frame_length);
#else
uint8_t *b=(uint8_t *)frame;
for(int i=0;i<frame->frame_length;i++) {
if((i%16)==0) printf("\n%04x: ",i);
printf("%02x ",b[i]);
}
printf("\n");
#endif
}
}
void Esp32_WLAN_insert_frame(Esp32WifiState *s, struct mac80211_frame *frame)
{
struct mac80211_frame *i_frame;
insertCRC(frame);
if(DEBUG) {
ANSI_FG_HCOLOR(GREEN);
printf("---------------\n>IN Send Frame type:%d subtype:%d channel:%d ap_state:%d time:%ld \n",frame->frame_control.type,frame->frame_control.sub_type,esp32_wifi_channel,s->ap_state,(unsigned long) qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
ANSI_DEFAULT();
}
infoprint(frame);
s->inject_queue_size++;
i_frame = s->inject_queue;
if (!i_frame) {
s->inject_queue = frame;
} else {
while (i_frame->next_frame) {
i_frame = i_frame->next_frame;
}
i_frame->next_frame = frame;
}
if (!s->inject_timer_running) {
// if the injection timer is not
// running currently, let's schedule
// one run...
s->inject_timer_running = 1;
timer_mod(s->inject_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + INTER_FRAME_TIME);
}
}
static _Bool Esp32_WLAN_can_receive(NetClientState *ncs)
{
Esp32WifiState *s = qemu_get_nic_opaque(ncs);
/*
if (s->ap_state != Esp32_WLAN__STATE_ASSOCIATED && s->ap_state != Esp32_WLAN__STATE_STA_ASSOCIATED) {
// we are currently not connected
// to the access point
return 0;
}
*/
if (s->inject_queue_size > Esp32_WLAN__MAX_INJECT_QUEUE_SIZE) {
// overload, please give me some time...
return 0;
}
return 1;
}
static ssize_t Esp32_WLAN_receive(NetClientState *ncs,
const uint8_t *buf, size_t size)
{
Esp32WifiState *s = qemu_get_nic_opaque(ncs);
struct mac80211_frame *frame;
if (!Esp32_WLAN_can_receive(ncs)) {
// this should not happen, but in
// case it does, let's simply drop
// the packet
return -1;
}
if (!s) {
return -1;
}
/*
* A 802.3 packet comes from the qemu network. The
* access points turns it into a 802.11 frame and
* forwards it to the wireless device
*/
frame = Esp32_WLAN_create_data_packet(s, buf, size);
if (frame) {
if(s->mode == Esp32_Mode_Station){
memcpy(s->ap_macaddr,s->associated_ap_macaddr,6);
if(s->ap_state==Esp32_WLAN__STATE_STA_ASSOCIATED) {
frame->frame_control.flags=1;
// if it's an arp request put the correct reply mac address in the packet
if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==6) {
memcpy(frame->data_and_fcs+16,s->macaddr,6);
}
}
}
else{
memcpy(frame->destination_address, s->softap_macaddr,6);
if(s->ap_state==Esp32_WLAN__STATE_STA_ASSOCIATED) {
frame->frame_control.flags=1;
// if it's an arp request put the correct reply mac address in the packet
if( frame->data_and_fcs[6]==8 && frame->data_and_fcs[7]==6) {
memcpy(frame->data_and_fcs+16,s->ap_macaddr,6);
}
}
}
if(((buf[12] & 0xE0) == IEEE80211_ENCAPSULATED)&&(buf[13] == ((IEEE80211_TYPE_MGT << 4)|IEEE80211_TYPE_MGT_SUBTYPE_ACTION)))
{
unsigned long ethernet_frame_size;
unsigned char ethernet_frame[1518];
struct mac80211_frame *reply = NULL;
//discard packages from others channels
if((buf[12] & 0x0F) != esp32_wifi_channel) return -1;
//discard packages originated from the same mac address
if(!memcmp(&buf[6],s->macaddr,6)) return -1;
//check destination
if((memcmp(&buf[0],BROADCAST,6))&&(memcmp(&buf[0],s->macaddr,6))) return -1;
//action frame
Esp32_WLAN_init_ap_frame(s, frame);
memcpy(frame->data_and_fcs, buf+14, size-14);
memcpy(frame->destination_address, &buf[0], 6);
memcpy(frame->source_address, &buf[6], 6);
memset(frame->bssid_address, 0xff, 6);
frame->frame_control.type= IEEE80211_TYPE_MGT;
frame->frame_control.sub_type= IEEE80211_TYPE_MGT_SUBTYPE_ACTION;
frame->frame_control.flags= ((buf[12] & 0xF0) == IEEE80211_ENCAPSULATED_PROTECTED) ? 0x40 : 00;
frame->duration_id= 0 ;
frame->frame_length-=12;
Esp32_WLAN_insert_frame(s, frame);
//if destination is broadcast, don´t send ack
if(!memcmp(&buf[0],BROADCAST,6)) return size;
//Send Ack
reply = Esp32_WLAN_create_ack();
memcpy(reply->destination_address, &buf[6], 6);
memcpy(reply->source_address, s->macaddr, 6);
memset(reply->bssid_address, 0xff, 6);
reply->duration_id=0;
if(DEBUG) {
ANSI_FG_HCOLOR(RED);
macprint(reply->destination_address,"---------------\n<OUT ACK send: dest ");
ANSI_DEFAULT();
}
infoprint(reply);
/*
* The access point uses the 802.11 frame
* and sends a 802.3 frame into the network...
* This packet is then understandable by
* qemu-slirp
*
* If we ever want the access point to offer
* some services, it can be added here!!
*/
// ethernet header type
ethernet_frame[12] = IEEE80211_ENCAPSULATED | esp32_wifi_channel;
ethernet_frame[13] = ((IEEE80211_TYPE_CTL << 4)|IEEE80211_TYPE_CTL_SUBTYPE_ACK);
memcpy(&ethernet_frame[0], reply->destination_address, 6);
memcpy(&ethernet_frame[6], reply->source_address, 6);
// add size of ethernet header
ethernet_frame_size = 14;
/*
* Send 802.3 frame
*/
qemu_send_packet(qemu_get_queue(s->nic), ethernet_frame, ethernet_frame_size);
return size;
}
if(((buf[12] & 0xF0)== IEEE80211_ENCAPSULATED)&&(buf[13] == ((IEEE80211_TYPE_CTL << 4)|IEEE80211_TYPE_CTL_SUBTYPE_ACK)))
{
//discard packages from others channels
if((buf[12] & 0x0F) != esp32_wifi_channel) return -1;
//discard packages originated from the same mac address
if(!memcmp(&buf[6],s->macaddr,6)) return -1;
//check destination
if(memcmp(&buf[0],s->macaddr,6)) return -1;
Esp32_WLAN_Set_Packet_Status(ESP32_PHYA_ACK);
timer_mod_anticipate(s->wait_ack_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
Esp32_WLAN_init_ap_frame(s, frame);
memcpy(frame->destination_address, &buf[0], 6);
memcpy(frame->source_address, &buf[6], 6);
memset(frame->bssid_address, 0xff, 6);
frame->frame_control.type= IEEE80211_TYPE_CTL;
frame->frame_control.sub_type= IEEE80211_TYPE_CTL_SUBTYPE_ACK;
frame->frame_control.flags= 0;
frame->duration_id= 0 ;
frame->frame_length = 10;
if(DEBUG) {
ANSI_FG_HCOLOR(GREEN);
macprint(&buf[6],"---------------\n>IN ACK received: source");
ANSI_DEFAULT();
}
infoprint(frame);
//ACK is not inserted in DMA
return size;
}
Esp32_WLAN_init_ap_frame(s, frame);
Esp32_WLAN_insert_frame(s, frame);
}
return size;
}
static void Esp32_WLAN_cleanup(NetClientState *ncs) { }
static NetClientInfo net_info = {
.type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = Esp32_WLAN_can_receive,
.receive = Esp32_WLAN_receive,
.cleanup = Esp32_WLAN_cleanup,
};
void Esp32_WLAN_reset_ap(Esp32WifiState *s) {
esp32_wifi_channel = 0;
s->ap_state = Esp32_WLAN__STATE_NOT_AUTHENTICATED;
s->beacon_ap=0;
s->mode = Esp32_Mode_Station;
memcpy(s->ap_macaddr,(uint8_t[]){0x10,0x13,0x46,0xbf,0x31,0x50},sizeof(s->ap_macaddr));
s->inject_timer_running = 0;
s->inject_sequence_number = 0;
s->inject_queue = NULL;
s->inject_queue_size = 0;
}
void Esp32_WLAN_setup_ap(DeviceState *dev,Esp32WifiState *s) {
Esp32_WLAN_reset_ap(s);
s->beacon_timer = timer_new_ns(QEMU_CLOCK_REALTIME, Esp32_WLAN_beacon_timer, s);
timer_mod(s->beacon_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME)+BEACON_TIME);
// setup the timer but only schedule
// it when necessary...
s->inject_timer = timer_new_ns(QEMU_CLOCK_REALTIME, Esp32_WLAN_inject_timer, s);
s->wait_ack_timer = timer_new_ns(QEMU_CLOCK_REALTIME, Esp32_WLAN_Wait_ACk_timer, s);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
memcpy(s->macaddr, s->conf.macaddr.a, sizeof(s->macaddr));
s->nic = qemu_new_nic(&net_info, &s->conf,
object_get_typename(OBJECT(s)), dev->id,
&dev->mem_reentrancy_guard, s);
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->macaddr);
}
static void send_single_frame(Esp32WifiState *s, struct mac80211_frame *frame, struct mac80211_frame *reply) {
reply->sequence_control.sequence_number = s->inject_sequence_number++ +0x730;
reply->signal_strength=-10;
if(frame) {
memcpy(reply->destination_address, frame->source_address, 6);
if(s->mode == Esp32_Mode_Station){
memcpy(reply->source_address, s->macaddr, 6);
}
else{
memcpy(reply->source_address, s->ap_macaddr, 6);
}
memcpy(reply->bssid_address, frame->source_address, 6);
}
Esp32_WLAN_insert_frame(s, reply);
}
void Esp32_WLAN_handle_frame(Esp32WifiState *s, struct mac80211_frame *frame)
{
struct mac80211_frame *reply = NULL;
static access_point_info dummy_ap={0};
static char ssid[64];
unsigned long ethernet_frame_size;
unsigned char ethernet_frame[1518];
if(DEBUG){
if((ENABLE_BEACON)||!((frame->frame_control.type == IEEE80211_TYPE_MGT)&&(frame->frame_control.sub_type == IEEE80211_TYPE_MGT_SUBTYPE_BEACON))){
ANSI_FG_HCOLOR(RED);
printf("-------------------------\n<OUT Handle Frame type:%d subtype:%d channel:%d ap_state:%d time:%ld \n",frame->frame_control.type,
frame->frame_control.sub_type,esp32_wifi_channel,s->ap_state,(unsigned long) qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
ANSI_DEFAULT();
}
}
if((ENABLE_BEACON)||!((frame->frame_control.type == IEEE80211_TYPE_MGT)&&(frame->frame_control.sub_type == IEEE80211_TYPE_MGT_SUBTYPE_BEACON))){
infoprint(frame);
}
access_point_info *ap_info=0;
if(s->mode == Esp32_Mode_Station){
for(int i=0;i<nb_aps;i++)
if(access_points[i].channel==esp32_wifi_channel){
ap_info=&access_points[i];
break;
}
}
Esp32_WLAN_Set_Packet_Status(ESP32_PHYA_ACK);
if(frame->frame_control.type == IEEE80211_TYPE_MGT) {
switch(frame->frame_control.sub_type) {
case IEEE80211_TYPE_MGT_SUBTYPE_BEACON:
if(s->ap_state==Esp32_WLAN__STATE_NOT_AUTHENTICATED || s->ap_state==Esp32_WLAN__STATE_AUTHENTICATED) {
strncpy(ssid,(char *)frame->data_and_fcs+14,frame->data_and_fcs[13]);
if(DEBUG) printf("beacon from %s\n",ssid);
dummy_ap.ssid=ssid;
dummy_ap.channel = esp32_wifi_channel;
dummy_ap.sigstrength= -23;
memcpy(dummy_ap.mac_address,s->ap_macaddr,6) ;
memcpy(s->softap_macaddr,frame->bssid_address,6);
s->mode = Esp32_Mode_SoftAP;
s->ap_state=Esp32_WLAN__STATE_STA_NOT_AUTHENTICATED;
send_single_frame(s,frame,Esp32_WLAN_create_probe_request(&dummy_ap));
}
break;
case IEEE80211_TYPE_MGT_SUBTYPE_ACTION:
if(DEBUG) printf("action\n");
/*
* The access point uses the 802.11 frame
* and sends a 802.3 frame into the network...
* This packet is then understandable by
* qemu-slirp
*
* If we ever want the access point to offer
* some services, it can be added here!!
*/
// ethernet header type
ethernet_frame[12] = ((frame->frame_control.flags & 0x40) ? IEEE80211_ENCAPSULATED_PROTECTED : IEEE80211_ENCAPSULATED)| esp32_wifi_channel;
ethernet_frame[13] = ((IEEE80211_TYPE_MGT << 4)|IEEE80211_TYPE_MGT_SUBTYPE_ACTION);
memcpy(&ethernet_frame[0], frame->destination_address, 6);
memcpy(&ethernet_frame[6], s->macaddr, 6);
// add packet content
ethernet_frame_size = frame->frame_length -IEEE80211_HEADER_SIZE;
if (ethernet_frame_size > sizeof(ethernet_frame)) {
ethernet_frame_size = sizeof(ethernet_frame);
}
memcpy(&ethernet_frame[14], &frame->data_and_fcs[0], ethernet_frame_size);
// add size of ethernet header
ethernet_frame_size += 14;
/*
* Send 802.3 frame
*/
qemu_send_packet(qemu_get_queue(s->nic), ethernet_frame, ethernet_frame_size);
//if destination is not broadcast wait for ack
if(memcmp(&ethernet_frame[0],BROADCAST,6)){
Esp32_WLAN_Set_Packet_Status(ESP32_PHYA_NACK);
timer_mod(s->wait_ack_timer, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + WAIT_ACK_TIMEOUT);
return;
}
break;
case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_RESP:
ap_info=&dummy_ap;
strncpy(ssid,(char *)frame->data_and_fcs+14,frame->data_and_fcs[13]);
if(DEBUG) printf("probe resp from %s\n",ssid);
dummy_ap.ssid=ssid;
s->ap_state=Esp32_WLAN__STATE_STA_NOT_AUTHENTICATED;
send_single_frame(s,frame,Esp32_WLAN_create_deauthentication());
send_single_frame(s,frame,Esp32_WLAN_create_authentication_request());
break;
case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_RESP:
if(DEBUG) printf("assoc resp\n");
mac80211_frame *frame1=Esp32_WLAN_create_dhcp_discover(s);
memcpy(frame1->bssid_address,BROADCAST,6);
memcpy(frame1->source_address,frame->destination_address,6);
memcpy(frame1->destination_address,frame->source_address,6);
send_single_frame(s,0,frame1);
s->ap_state=Esp32_WLAN__STATE_STA_DHCP;
break;
case IEEE80211_TYPE_MGT_SUBTYPE_DISASSOCIATION:
DEBUG_PRINT_AP(("Received disassociation!\n"));
send_single_frame(s,frame,Esp32_WLAN_create_disassociation());
if (s->ap_state == Esp32_WLAN__STATE_ASSOCIATED || s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) {
s->ap_state = Esp32_WLAN__STATE_AUTHENTICATED;
}
break;
case IEEE80211_TYPE_MGT_SUBTYPE_DEAUTHENTICATION:
DEBUG_PRINT_AP(("Received deauthentication!\n"));
//reply = Esp32_WLAN_create_authentication_response(ap_info);
if (s->ap_state == Esp32_WLAN__STATE_AUTHENTICATED) {
s->ap_state = Esp32_WLAN__STATE_NOT_AUTHENTICATED;
}
break;
case IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION:
DEBUG_PRINT_AP(("Received authentication!\n"));
if(frame->data_and_fcs[2]==2) { // response
send_single_frame(s,frame,Esp32_WLAN_create_association_request(&dummy_ap));
}
break;
}
if(ap_info) {
memcpy(s->ap_macaddr,ap_info->mac_address,6);
switch(frame->frame_control.sub_type) {
case IEEE80211_TYPE_MGT_SUBTYPE_PROBE_REQ:
DEBUG_PRINT_AP(("Received probe request!\n"));
reply = Esp32_WLAN_create_probe_response(ap_info);
break;
case IEEE80211_TYPE_MGT_SUBTYPE_AUTHENTICATION:
DEBUG_PRINT_AP(("Received authentication req!\n"));
if(frame->data_and_fcs[2]==1) { // request
reply = Esp32_WLAN_create_authentication_response(ap_info);
if (s->ap_state == Esp32_WLAN__STATE_NOT_AUTHENTICATED) {
s->ap_state = Esp32_WLAN__STATE_AUTHENTICATED;
}
}
break;
case IEEE80211_TYPE_MGT_SUBTYPE_ASSOCIATION_REQ:
DEBUG_PRINT_AP(("Received association request!\n"));
reply = Esp32_WLAN_create_association_response(ap_info);
if (s->ap_state == Esp32_WLAN__STATE_AUTHENTICATED) {
s->ap_state = Esp32_WLAN__STATE_ASSOCIATED;
memcpy(s->associated_ap_macaddr,s->ap_macaddr,6);
}
break;
}
if (reply) {
reply->signal_strength=ap_info->sigstrength;
memcpy(reply->destination_address, frame->source_address, 6);
Esp32_WLAN_init_ap_frame(s, reply);
Esp32_WLAN_insert_frame(s, reply);
}
}
}
if ((frame->frame_control.type == IEEE80211_TYPE_DATA) &&
(frame->frame_control.sub_type == IEEE80211_TYPE_DATA_SUBTYPE_DATA)) {
if(s->ap_state == Esp32_WLAN__STATE_STA_DHCP) {
dhcp_request_t *req=(dhcp_request_t *)&frame->data_and_fcs[8];
// check for a dhcp offer
if(req->dhcp.bp_options[0]==0x35 && req->dhcp.bp_options[2]==0x2) {
mac80211_frame *frame1=Esp32_WLAN_create_dhcp_request(s,req->dhcp.yiaddr);
memcpy(frame1->bssid_address,BROADCAST,6);
memcpy(frame1->source_address,s->ap_macaddr,6);
memcpy(frame1->destination_address,frame->source_address,6);
send_single_frame(s,0,frame1);
//memcpy(s->ap_macaddr,(uint8_t[]){0x10,0x01,0x00,0xc4,0x0a,0x25},sizeof(s->ap_macaddr));
//memcpy(s->associated_ap_macaddr,s->ap_macaddr,sizeof(s->ap_macaddr));
memcpy(s->associated_ap_macaddr,s->macaddr,sizeof(s->macaddr));
s->ap_state=Esp32_WLAN__STATE_STA_ASSOCIATED;
if(DEBUG) printf("Dhcp received addr %d.%d.%d.%d\n",req->dhcp.yiaddr[0],req->dhcp.yiaddr[1],req->dhcp.yiaddr[2],req->dhcp.yiaddr[3]);
}
} else if (s->ap_state == Esp32_WLAN__STATE_ASSOCIATED || s->ap_state == Esp32_WLAN__STATE_STA_ASSOCIATED) {
/*
* The access point uses the 802.11 frame
* and sends a 802.3 frame into the network...
* This packet is then understandable by
* qemu-slirp
*
* If we ever want the access point to offer
* some services, it can be added here!!
*/
// ethernet header type
ethernet_frame[12] = frame->data_and_fcs[6];
ethernet_frame[13] = frame->data_and_fcs[7];
// the new originator of the packet is
// the access point
if(s->ap_state == Esp32_WLAN__STATE_ASSOCIATED)
memcpy(&ethernet_frame[6], s->ap_macaddr, 6);
else
memcpy(&ethernet_frame[6], s->macaddr, 6);
if (ethernet_frame[12] == 0x08 && ethernet_frame[13] == 0x06) {
// for arp request, we use a broadcast
memset(&ethernet_frame[0], 0xff, 6);
} else {
// otherwise we forward the packet to
// where it really belongs
memcpy(&ethernet_frame[0], frame->destination_address, 6);
}
// add packet content
ethernet_frame_size = frame->frame_length - IEEE80211_HEADER_SIZE - 4 - 8;
// for some reason, the packet is 22 bytes too small (??)
ethernet_frame_size += 22;
if (ethernet_frame_size > sizeof(ethernet_frame)) {
ethernet_frame_size = sizeof(ethernet_frame);
}
memcpy(&ethernet_frame[14], &frame->data_and_fcs[8], ethernet_frame_size);
// add size of ethernet header
ethernet_frame_size += 14;
/*
* Send 802.3 frame
*/
qemu_send_packet(qemu_get_queue(s->nic), ethernet_frame, ethernet_frame_size);
}
}
Esp32_WLAN_frame_delivered(s);
}