blob: 277af194a1cf49925bd5eed3678631ba7599d2b8 [file] [log] [blame]
/*
* Sigma Control API DUT (station/AP)
* Copyright (c) 2018, The Linux Foundation
* All Rights Reserved.
* Licensed under the Clear BSD license. See README for more details.
*/
#include "sigma_dut.h"
#include "wpa_helpers.h"
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <pcap.h>
#include <signal.h>
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_ACK 5
#define UDP_PROTOCOL 17
static pcap_t *pcap = NULL;
struct dhcp_pkt {
struct iphdr iph;
struct udphdr udph;
u8 op;
u8 htype;
u8 hlen;
u8 hops;
u32 xid;
u16 secs;
u16 flags;
u32 client_ip;
u32 your_ip;
u32 server_ip;
u32 relay_ip;
u8 hw_addr[16];
u8 serv_name[64];
u8 boot_file[128];
u32 magic_cookie;
u8 options[314];
} __attribute__ ((packed));
enum dhcp_options {
DHCP_OPT_SUBNET_MASK = 1,
DHCP_OPT_ROUTER = 3,
DHCP_OPT_MSG_TYPE = 53,
DHCP_OPT_END = 255
};
static u8 * get_dhcp_option(u8 *options, u8 type, int len)
{
u8 *pos = options;
u8 *end = pos + len;
while (pos < end && pos + 1 < end && pos + 2 + pos[1] <= end) {
if (*pos == type)
return pos;
pos += 2 + pos[1];
}
return NULL;
}
/**
* 1. Open UDP socket
* 2. read_msg
* 3. Process DHCP_ACK
*/
static void * process_dhcp_ack(void *ptr)
{
struct bpf_program pcap_fp;
char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
struct sigma_dut *dut = ptr;
int nbytes = 0;
u8 buf[1024];
struct dhcp_pkt *dhcp;
int option_len;
u8 *msg_type, *val;
struct in_addr ip;
char your_ip[16], mask[16], router[16];
unsigned short protocol, port_no;
bpf_u_int32 pcap_maskp, pcap_netp;
char ifname[20];
protocol = UDP_PROTOCOL;
port_no = DHCP_SERVER_PORT;
strlcpy(ifname, get_main_ifname(dut), sizeof(ifname));
/* gives the network mask for ifname essential for applying filter */
pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err);
sigma_dut_print(dut, DUT_MSG_INFO, "DHCP: ifname = %s", ifname);
/* creates a session for sniffing */
pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err);
if (!pcap) {
sigma_dut_print(dut, DUT_MSG_INFO, "pcap_open_live: %s",
pcap_err);
goto exit;
}
snprintf(pcap_filter, sizeof(pcap_filter),
"ip proto 0x%x and udp src port 0x%x",
protocol, port_no);
sigma_dut_print(dut, DUT_MSG_INFO, "pcap_flter %s", pcap_filter);
if (pcap_compile(pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0)
sigma_dut_print(dut, DUT_MSG_INFO, "pcap_compile: %s",
pcap_geterr(pcap));
if (pcap_setfilter(pcap, &pcap_fp) < 0)
sigma_dut_print(dut, DUT_MSG_INFO, "pcap_setfilter: %s",
pcap_geterr(pcap));
pcap_freecode(&pcap_fp);
while (1) {
memset(buf, 0, sizeof(buf));
sigma_dut_print(dut, DUT_MSG_DEBUG,
"HLP: Waiting for message to receive");
nbytes = recvfrom(pcap_get_selectable_fd(pcap), &buf,
sizeof(buf), 0, NULL, NULL);
if (nbytes == -1) {
sigma_dut_print(dut, DUT_MSG_ERROR, "HLP: failed: %s",
strerror(errno));
goto exit;
}
sigma_dut_print(dut, DUT_MSG_INFO, "HLP: Received %d bytes",
nbytes);
hex_dump(dut, buf, nbytes);
if (nbytes < 314) {
sigma_dut_print(dut, DUT_MSG_DEBUG,
"HLP: Ignore MSG, Too short message received");
continue;
}
nbytes -= 14;
/*
* Process DHCP packet
* skip ethernet header from buf and then process the ack
*/
dhcp = (struct dhcp_pkt *) (buf + ETH_HLEN);
option_len = nbytes - ((char *) dhcp->options - (char *) dhcp);
sigma_dut_print(dut, DUT_MSG_DEBUG,
"option_len %d, First option : %02x",
option_len, *(dhcp->options));
/* Check for DHCP_ACK */
msg_type = get_dhcp_option(dhcp->options, DHCP_OPT_MSG_TYPE,
option_len);
if (!msg_type) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"Ignore MSG, DHCP OPT MSG_TYPE missing");
continue;
}
if (msg_type[2] != DHCP_ACK) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"Ignore MSG, DHCP message type : %02x",
msg_type[2]);
continue;
}
ip.s_addr = dhcp->your_ip;
strlcpy(your_ip, inet_ntoa(ip), sizeof(your_ip));
val = get_dhcp_option(dhcp->options, DHCP_OPT_SUBNET_MASK,
option_len);
if (!val) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"DHCP option SUBNET_MASK missing");
continue;
}
memcpy(&(ip.s_addr), (val + 2), val[1]);
strlcpy(mask, inet_ntoa(ip), sizeof(mask));
val = get_dhcp_option(dhcp->options, DHCP_OPT_ROUTER,
option_len);
if (!val) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"DHCP option DHCP_OPT_ROUTER missing");
continue;
}
memcpy(&(ip.s_addr), val + 2, val[1]);
strlcpy(router, inet_ntoa(ip), sizeof(router));
sigma_dut_print(dut, DUT_MSG_DEBUG,
"OP: %d, your_ip: %s, netmask: %s, router: %s",
dhcp->op, your_ip, mask, router);
/* set ip configuration */
if (!set_ipv4_addr(dut, ifname, your_ip, mask)) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"Failed to set IP address");
continue;
}
if (set_ipv4_gw(dut, router) < 1) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"Failed to set Gateway address");
continue;
}
sigma_dut_print(dut, DUT_MSG_DEBUG,
"IP configuration completed");
}
exit:
sigma_dut_print(dut, DUT_MSG_INFO, "HLP: Received failed in exit");
if (pcap) {
pcap_close(pcap);
pcap = NULL;
}
dut->hlp_thread = 0;
return NULL;
}
static void hlp_thread_exit(int signum)
{
pthread_exit(0);
}
void hlp_thread_cleanup(struct sigma_dut *dut)
{
if (pcap) {
pcap_close(pcap);
pcap = NULL;
}
if (dut->hlp_thread) {
sigma_dut_print(dut, DUT_MSG_DEBUG, "Kill thread: %ld",
dut->hlp_thread);
pthread_kill(dut->hlp_thread, SIGUSR1);
dut->hlp_thread = 0;
}
}
void process_fils_hlp(struct sigma_dut *dut)
{
static pthread_t hlp_thread;
if (dut->hlp_thread) {
sigma_dut_print(dut, DUT_MSG_ERROR,
"FILS-HLP DHCP thread already running");
return;
}
signal(SIGUSR1, hlp_thread_exit);
sigma_dut_print(dut, DUT_MSG_DEBUG, "Creating FILS_HLP thread-->");
/* create FILS_HLP thread */
if (!pthread_create(&hlp_thread, NULL, &process_dhcp_ack,
(void *) dut)) {
dut->hlp_thread = hlp_thread;
} else {
sigma_dut_print(dut, DUT_MSG_DEBUG,
"FILS_HLP thread creation failed");
}
}