/* Blatantly copied from iwpriv from the iwtools set
 *
 * Rev 08
 * 
 * Goal: Consistently formatted iwpriv output for fingerprinting drivers
 * dragorn 2005

 *	Wireless Tools
 *
 *		Jean II - HPLB 97->99 - HPL 99->03
*/

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include <net/if_arp.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>

#include <stdint.h>

#include <linux/wireless.h>

/* Known fingerprints */
typedef struct {
	char *fingerprint;
	char *cardname;
	int checksum;
} fp;

fp **fingerprints;
int nfingerprints;

void populate_fingerprints() {
	fp *tfp;

	/* Hardcoded, change this when adding */
	nfingerprints = 15;

	fingerprints = (fp **) malloc(sizeof(fp *) * nfingerprints);

	/* Populate them */
	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[0] = tfp;
	tfp->fingerprint = strdup("set_power8BE01int0noneget_power8BE10none80charset_mode8BE21int0noneget_mode8BE30none80charset_preamble8BE41int0noneget_preamble8BE50none16charreset8BE60int0nonesw_reset8BE70int0nonemonitor8BE82int0none");
	tfp->checksum = 0xDBE1490C;
	tfp->cardname = strdup("IPW2200 1.0.4");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[1] = tfp;
	tfp->fingerprint = strdup("airoioctl8BE012byte2047byteairoidifc8BE112byte1int");
	tfp->checksum = 0xC003117A;
	tfp->cardname = strdup("Cisco 2.6.11 kernel driver");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[2] = tfp;
	tfp->fingerprint = strdup("setoptie8BE8256byte0nonegetoptie8BE90none256bytesetkey8BE260byte0nonedelkey8BE47byte0nonesetmlme8BE610byte0noneaddmac8BEA1addr0nonedelmac8BEC1addr0nonechanlist8BEE32byte0nonesetparam8BE02int0nonegetparam8BE11int1intturbo11int0noneget_turbo10none1intmode21int0noneget_mode20none1intauthmode31int0noneget_authmode30none1intprotmode41int0noneget_protmode40none1intmcastcipher51int0noneget_mcastcipher50none1intmcastkeylen61int0noneget_mcastkeylen60none1intucastciphers71int0noneget_uciphers70none1intucastcipher81int0noneget_ucastcipher80none1intucastkeylen91int0noneget_ucastkeylen90none1intkeymgtalgs151int0noneget_keymgtalgs150none1intrsncaps161int0noneget_rsncaps160none1introamingC1int0noneget_roamingC0none1intprivacyD1int0noneget_privacyD0none1intcountermeasuresE1int0noneget_countermeasE0none1intdropunencryptedF1int0noneget_dropunencryF0none1intwpaA1int0noneget_wpaA0none1intdriver_caps101int0noneget_driver_caps100none1intmaccmd111int0nonewme121int0noneget_wme120none1inthide_ssid131int0noneget_hide_ssid130none1intap_bridge141int0noneget_ap_bridge140none1intinact171int0noneget_inact170none1intinact_auth181int0noneget_inact_auth180none1intinact_init191int0noneget_inact_init190none1int");
	tfp->checksum = 0x767DC4E8;
	tfp->cardname = strdup("MadWifi 0.9.4.12");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[3] = tfp;
	tfp->fingerprint = strdup("monitor8BE41int0nonereadmif8BE31byte1bytewritemif8BE22byte0nonereset8BE61int0noneinquire8BE81int0noneset_rid_word8BEE2int0nonemaccmd8BF01int0nonewds_add8BEA1addr0nonewds_del8BEC1addr0noneaddmac8BF21addr0nonedelmac8BF41addr0nonekickmac8BF61addr0noneprism2_param8BE02int0nonegetprism2_param8BE11int1inttxratectrl21int0nonegettxratectrl20none1intbeacon_int31int0nonegetbeacon_int30none1intpseudo_ibss41int0nonegetpseudo_ibss40none1intalc51int0nonegetalc50none1intdump71int0nonegetdump70none1intother_ap_policy81int0nonegetother_ap_pol80none1intmax_inactivity91int0nonegetmax_inactivi90none1intbridge_packetsA1int0nonegetbridge_packeA0none1intdtim_periodB1int0nonegetdtim_periodB0none1intnullfunc_ackC1int0nonegetnullfunc_ackC0none1intmax_wdsD1int0nonegetmax_wdsD0none1intautom_ap_wdsE1int0nonegetautom_ap_wdsE0none1intap_auth_algsF1int0nonegetap_auth_algsF0none1intallow_fcserr101int0nonegetallow_fcserr100none1inthost_encrypt111int0nonegethost_encrypt110none1inthost_decrypt121int0nonegethost_decrypt120none1intbusmaster_rx131int0nonegetbusmaster_rx130none1intbusmaster_tx141int0nonegetbusmaster_tx140none1inthost_roaming151int0nonegethost_roaming150none1intbcrx_sta_key161int0nonegetbcrx_sta_key160none1intieee_802_1x171int0nonegetieee_802_1x170none1intantsel_tx181int0nonegetantsel_tx180none1intantsel_rx191int0nonegetantsel_rx190none1intmonitor_type1A1int0nonegetmonitor_type1A0none1intwds_type1B1int0nonegetwds_type1B0none1inthostscan1C1int0nonegethostscan1C0none1intap_scan1D1int0nonegetap_scan1D0none1intenh_sec1E1int0nonegetenh_sec1E0none1intbasic_rates201int0nonegetbasic_rates200none1intoper_rates211int0nonegetoper_rates210none1inthostapd221int0nonegethostapd220none1inthostapd_sta231int0nonegethostapd_sta230none1intwpa241int0nonegetwpa240none1intprivacy_invoked251int0nonegetprivacy_invo250none1inttkip_countermea261int0nonegettkip_counter260none1intdrop_unencrypte271int0nonegetdrop_unencry270none1int");
	tfp->checksum = 0x52DED790;
	tfp->cardname = strdup("HostAP 0.2.5");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[4] = tfp;
	tfp->fingerprint = strdup("force_reset8BE00none0nonecard_reset8BE10none0noneset_port38BE21int0noneget_port38BE30none1intset_preamble8BE41int0noneget_preamble8BE50none1intset_ibssport8BE61int0noneget_ibssport8BE70none1intmonitor8BE82int0nonedump_recs8BFF0none0none");
	tfp->checksum = 0x3CA85717;
	tfp->cardname = strdup("Orinoco_cs 0.13e+Dragorn3");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[5] = tfp;
	tfp->fingerprint = strdup("reset8BE00none0noneget_prismhdr8BF70none1intset_prismhdr8BF81int0nonegetPolicy8BE10none1intsetPolicy8BE21int0nonegetMac8BE30none64addraddMac8BE41addr0nonedelMac8BE61addr0nonekickMac8BE81addr0nonekickAll8BEA0none0noneget_wpa8BEB0none1intset_wpa8BEC1int0nonedbg_oid8BEE1int0nonedbg_get_oid8BEF0none256bytedbg_set_oid8BF0256byte0nones_addr01addr0noneg_addr00none1024charg_linkstate10none1024chars_bsstype61int0noneg_bsstype60none1024chars_bssid71addr0noneg_bssid70none1024chars_state91int0noneg_state90none1024chars_aidA1int0noneg_aidA0none1024chars_ssidoverrideC1char0noneg_ssidoverrideC0none1024chars_medlimitD1int0noneg_medlimitD0none1024chars_beaconE1int0noneg_beaconE0none1024chars_dtimperiodF1int0noneg_dtimperiodF0none1024chars_authenable141int0noneg_authenable140none1024chars_privinvok151int0noneg_privinvok150none1024chars_exunencrypt161int0noneg_exunencrypt160none1024chars_rekeythresh1A1int0noneg_rekeythresh1A0none1024chars_maxtxlife241int0noneg_maxtxlife240none1024chars_maxrxlife251int0noneg_maxrxlife250none1024chars_fixedrate2C1int0noneg_fixedrate2C0none1024chars_frameburst301int0noneg_frameburst300none1024chars_psm311int0noneg_psm310none1024chars_bridge351int0noneg_bridge350none1024chars_clients361int0noneg_clients360none1024chars_clientassoc371int0noneg_clientassoc370none1024chars_dot1xenable3E1int0noneg_dot1xenable3E0none1024chars_rxant521int0noneg_rxant520none1024chars_txant531int0noneg_txant530none1024chars_antdivers541int0noneg_antdivers540none1024chars_edthresh561int0noneg_edthresh560none1024chars_preamble571int0noneg_preamble570none1024charg_rates580none1024chars_.11outpower5D1int0noneg_.11outpower5D0none1024charg_supprates5E0none1024charg_suppfreq600none1024chars_noisefloor611int0noneg_noisefloor610none1024charg_freqactivity620none1024chars_nonerpprotec641int0noneg_nonerpprotec640none1024chars_profile671int0noneg_profile670none1024charg_extrates680none1024chars_mlmelevel781int0noneg_mlmelevel780none1024charg_bsss7C0none1024charg_bsslist7F0none1024chars_mode831int0noneg_mode830none1024chars_config881int0noneg_config880none1024chars_.11dconform891int0noneg_.11dconform890none1024charg_phycapa8A0none1024chars_outpower8B1int0noneg_outpower8B0none1024char");
	tfp->checksum = 0x16FA1D42;
	tfp->cardname = strdup("Prism54 2.6.11 kernel");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[6] = tfp;
	tfp->fingerprint = strdup("setoptie8BE8256byte0nonegetoptie8BE90none256bytesetkey8BE260byte0nonedelkey8BE47byte0nonesetmlme8BE642byte0noneaddmac8BEA1addr0nonedelmac8BEC1addr0nonechanlist8BEE32byte0nonesetparam8BE02int0nonegetparam8BE11int1intturbo11int0noneget_turbo10none1intmode21int0noneget_mode20none1intauthmode31int0noneget_authmode30none1intprotmode41int0noneget_protmode40none1intmcastcipher51int0noneget_mcastcipher50none1intmcastkeylen61int0noneget_mcastkeylen60none1intucastciphers71int0noneget_uciphers70none1intucastcipher81int0noneget_ucastcipher80none1intucastkeylen91int0noneget_ucastkeylen90none1intkeymgtalgs151int0noneget_keymgtalgs150none1intrsncaps161int0noneget_rsncaps160none1introamingC1int0noneget_roamingC0none1intprivacyD1int0noneget_privacyD0none1intcountermeasuresE1int0noneget_countermeasE0none1intdropunencryptedF1int0noneget_dropunencryF0none1intwpaA1int0noneget_wpaA0none1intdriver_caps101int0noneget_driver_caps100none1intmaccmd111int0nonewme121int0noneget_wme120none1inthide_ssid131int0noneget_hide_ssid130none1intap_bridge141int0noneget_ap_bridge140none1intinact171int0noneget_inact170none1intinact_auth181int0noneget_inact_auth180none1intinact_init191int0noneget_inact_init190none1intibss1A1int0noneget_ibss1A0none1intreset631int0none");
	tfp->checksum = 0xD9A6D772;
	tfp->cardname = strdup("Madwifi 0.8.5.0-BSD/0.9.5.0-BSD");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[7] = tfp;
	tfp->fingerprint = strdup("monitor8BE02int0nonereset8BE10int0noneset_power8BE21int0noneget_power8BE30none80charset_preamble8BE41int0noneget_preamble8BE50none16char");
	tfp->checksum = 0x516D318D;
	tfp->cardname = strdup("ipw2100 1.0.3");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[8] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0xB4E44FBB;
	tfp->cardname = strdup("Orinoco_cs kernel 2.6.11");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[9] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0x555CEA89;
	tfp->cardname = strdup("HostAP 0.4.1");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[10] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0xEC2AD776;
	tfp->cardname = strdup("Madwifi-BSD 0.9.5.0");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[11] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0x77AE3380;
	tfp->cardname = strdup("ipw2200 old/no monitor");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[12] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0xDB49207E;
	tfp->cardname = strdup("ipw2200 2.6.12/no monitor");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[13] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0x64481897;
	tfp->cardname = strdup("rt2570/ralink");

	tfp = (fp *) malloc(sizeof(fp));
	fingerprints[14] = tfp;
	tfp->fingerprint = NULL;
	tfp->checksum = 0xAE7F1461;
	tfp->cardname = strdup("st2x00/ralink");
}

/* stolen from rsync, stolen from Adler-32 */
#define CHAR_OFFSET 0
uint32_t get_checksum1(char *buf1,int len) {
	int i;
	uint32_t s1, s2;
	char *buf = (char *)buf1;

	s1 = s2 = 0;
	for (i = 0; i < (len-4); i+=4) {
		s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 
			10*CHAR_OFFSET;
		s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET); 
	}
	for (; i < len; i++) {
		s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
	}
	return (s1 & 0xffff) + (s2 << 16);
}

#define IW_MAX_PRIV_DEF 128
#define LINUX_WLEXT_MONITOR 6
#define LINUX_WLEXT_MASTER  3

typedef struct iw_priv_args iwprivargs;

static const char *	argtype[] = {
  "none", "byte", "char", "", "int", "float", "addr" };

static inline int
iw_get_ext(int			skfd,		/* Socket to the kernel */
	   char *		ifname,		/* Device name */
	   int			request,	/* WE ID */
	   struct iwreq *	pwrq)		/* Fixed part of the request */
{
  /* Set device name */
  strncpy(pwrq->ifr_name, ifname, IFNAMSIZ);
  /* Do the request */
  return(ioctl(skfd, request, pwrq));
}

int
iw_sockets_open(void)
{
  static const int families[] = {
    AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
  };
  unsigned int	i;
  int		sock;

  /*
   * Now pick any (exisiting) useful socket family for generic queries
   * Note : don't open all the socket, only returns when one matches,
   * all protocols might not be valid.
   * Workaround by Jim Kaba <jkaba@sarnoff.com>
   * Note : in 99% of the case, we will just open the inet_sock.
   * The remaining 1% case are not fully correct...
   */

  /* Try all families we support */
  for(i = 0; i < sizeof(families)/sizeof(int); ++i)
    {
      /* Try to open the socket, if success returns it */
      sock = socket(families[i], SOCK_DGRAM, 0);
      if(sock >= 0)
	return sock;
  }

  return -1;
}
int
iw_get_priv_info(int		skfd,
		 char *		ifname,
		 iwprivargs *	priv,
		 int		maxpriv)
{
  struct iwreq		wrq;

  /* Ask the driver */
  wrq.u.data.pointer = (caddr_t) priv;
  wrq.u.data.length = maxpriv;
  wrq.u.data.flags = 0;
  if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0)
    return(-1);

  /* Return the number of ioctls */
  return(wrq.u.data.length);
}

	static int
print_priv_info(int		skfd,
				char *		ifname,
				char *		args[],
				int		count)
{
	int		k;
	iwprivargs	priv[IW_MAX_PRIV_DEF];
	int		n;
	int 	match;
	char buffer[8192] = "\0";
	char sig[8192] = "\0";
	uint32_t checksum;

	/* Avoid "Unused parameter" warning */
	args = args; count = count;

	/* Read the private ioctls */
	n = iw_get_priv_info(skfd, ifname, priv, IW_MAX_PRIV_DEF);

	/* Print them all */
	for(k = 0; k < n; k++)
		if(priv[k].name[0] != '\0') {
			snprintf(buffer, 8192,"%s%s%X%d%s%d%s",
					 sig,
					 priv[k].name, priv[k].cmd,
					 priv[k].set_args & IW_PRIV_SIZE_MASK,
					 argtype[(priv[k].set_args & IW_PRIV_TYPE_MASK) >> 12],
					 priv[k].get_args & IW_PRIV_SIZE_MASK,
					 argtype[(priv[k].get_args & IW_PRIV_TYPE_MASK) >> 12]);
			strncpy(sig, buffer, 8192);
		}

	printf("%s\n", sig);
	checksum = get_checksum1(sig, strlen(sig));
	printf("Checksum: %X\n", (unsigned int) checksum);

	for (match = 0; match < nfingerprints; match++) {
		if (checksum == fingerprints[match]->checksum) {
			printf("Card looks like a '%s' checksum %X.  If this isn't accurate, tell dragorn.  If it is, tell him too.\n",
				   fingerprints[match]->cardname, (unsigned int) checksum);
			return(0);
		}
	}

	printf("Couldn't detect card type, let dragorn know the above fingerprint\n");

	return(0);
}

int main(int argc, char *argv[]) {
	int skfd;

	if (argc == 1) {
		populate_fingerprints();
		printf("Known fingerprints (run %s <interface> to test):\n", argv[0]);
		for (skfd = 0; skfd < nfingerprints; skfd++) {
			printf("Card %s checksum %X\n", fingerprints[skfd]->cardname,
				   fingerprints[skfd]->fingerprint ?
				   get_checksum1(fingerprints[skfd]->fingerprint, 
								 strlen(fingerprints[skfd]->fingerprint)) : 
				   fingerprints[skfd]->checksum);
		}
		return 0;
	}
	
	/* Create a channel to the NET kernel. */
	if((skfd = iw_sockets_open()) < 0)
	{
		perror("socket");
		return(-1);
	}

	/* No argument : show the list of all device + info */
	if(argc == 2) {
		populate_fingerprints();
	    print_priv_info(skfd, argv[1], NULL, 0);
	} else {
		perror("no device given/invalid arguments");
		return(-1);
	}

	return 0;
}

