/**
 * A client-side 802.1x implementation 
 *
 * This code is released under both the GPL version 2 and BSD licenses.
 * Either license may be used.  The respective licenses are found below.
 *
 * Copyright (C) 2002 Bryan D. Payne & Nick L. Petroni Jr.
 * All Rights Reserved
 *
 * --- GPL Version 2 License ---
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * --- BSD License ---
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  - All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *       This product includes software developed by the University of
 *       Maryland at College Park and its contributors.
 *  - Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*******************************************************************
 * The driver function for a Linux application layer EAPOL 
 * implementation
 * File: cardif_linux.c
 *
 * Authors: Chris.Hessing@utah.edu
 *
 * $Id: cardif_linux.c,v 1.43 2004/08/20 04:42:28 chessing Exp $
 * $Date: 2004/08/20 04:42:28 $
 * $Log: cardif_linux.c,v $
 * Revision 1.43  2004/08/20 04:42:28  chessing
 * Set all of the new scanning and WPA code to be disabled by default, so that a 1.0.1 release can be made.  Added additional error message output to AKA code.  Fixed a few memory leaks in the AKA code.
 *
 * Revision 1.42  2004/08/19 04:11:48  chessing
 * More updates to get closer to WPA support.  We can now associate to an AP that has WPA, however we are using a static WPA IE, so that needs to be fixed.  There is also still a fair bit of other things that need to be cleaned up, but we are closer.
 *
 * Revision 1.41  2004/08/19 02:28:07  chessing
 * First piece of WPA patch.  (The patch is growing fast, and this commit is to save what is done so far.)
 *
 * Revision 1.40  2004/08/16 23:36:28  chessing
 * Cosmetic fixes and trapping of an error that popped up.
 *
 * Revision 1.39  2004/08/16 01:56:44  chessing
 *
 * A few more updates to last nights patch.
 *
 * Revision 1.38  2004/08/15 04:29:35  chessing
 *
 * Completed support for scanning and joining wireless networks.  If your interface isn't configured, we will scan to gather information about all known wireless networks.  If a network in the list has a configuration, we will join it.  (Including turning on needed encryption.)  There are still a few rough edges that need to be hammered out.  But the overall user experience should be improved.
 *
 * Revision 1.37  2004/08/12 01:40:06  chessing
 *
 * Added "association" option to the global configuration file sections.  It can be set to either auto, or manual.  Setting "association" to auto means that XSupplicant will attempt to associate to a network with the lowest value in the "priority" setting.  The default for "association" is auto.  The other option added is "priority" which is a numeric value from 1..256.  A value of 1 is the highest priority, and will be the first network chosen.  (Assuming the network can be found in a scan.)  The default value for "priority" is 0, which means that network will be checked last.   --  Although the options are now in the config file, the actual functionality for auto association/priority is not yet complete.  (So, basically these options don't do anything yet. ;)
 *
 * Revision 1.36  2004/08/10 01:59:26  chessing
 *
 * Added support for the SNMP supplicant counters defined in the IEEE 802.1X-2001 document.
 *
 * Revision 1.35  2004/08/05 23:56:21  chessing
 *
 * Added basic support for scanning for broadcast SSIDs.  This is another step closer to WPA/11i support. ;)
 *
 * Revision 1.34  2004/07/26 03:08:58  chessing
 *
 * Fixed a few bugs in the new hot plug code.  Hot unplugging (and replugging) now works.
 *
 * Revision 1.33  2004/07/25 19:36:41  chessing
 * Fixed a few more logical/bitwise nots.  Added rtnetlink support for hot-plugging interfaces.  (Hot unplugging is coming soon. ;)
 *
 * Revision 1.32  2004/07/23 04:05:50  chessing
 * Fixed a segfault problem.  Started to add rtnetlink support.
 *
 * Revision 1.31  2004/07/22 22:35:07  chessing
 *
 * Changed cardif pieces to build as an intermediate module, to allow it to be defined in more than one file.  These changes should help make things more portable.
 *
 * Revision 1.30  2004/07/21 22:47:53  chessing
 *
 * Fixed bitwise not problem identified in bugid 995523.
 *
 * Revision 1.29  2004/07/19 02:43:16  chessing
 *
 * Changed things to get rid of Linux specific pieces in the interface_data struct. Fixed up EAP-SIM and EAP-AKA to skip AVPs that we don't support.  (We print a mesage, and move on.)  Added the --enable-radiator-test option to configure EAP-AKA to use the test vectors included with Radiator's AKA module.  (To use this mode, no SIM card is required, but the PCSC library is still needed to build.  Also, some errors will be displayed.)
 *
 * Revision 1.28  2004/07/15 05:49:22  chessing
 *
 * We now reset ALLMULTI to the state is was in when we start XSupplicant.  Also, when we have to reset keys, we now reset all of them, instead of just the first one.
 *
 * Revision 1.27  2004/07/15 04:15:35  chessing
 *
 * True/false int values are now bit flags in a byte.  PEAP now calls back in to functions from eap.c for phase 2 methods.  This allows for any phase 1 EAP type to work.  This resulted in some major changes the functions in eap.c, and in peap_phase2.c.  PEAP has been tested using both MS-CHAPv2, and EAP-MD5 as inner types.  More types need to be tested, and enabled.
 *
 * Revision 1.26  2004/06/21 05:19:12  chessing
 *
 * Added a few minor fixes to EAP-AKA support.  Added "allmulti" as a global configuration option.  (By default, allmulti is now on!)
 *
 * Revision 1.25  2004/06/15 03:22:29  chessing
 *
 * XSupplicant Release 1.0
 *
 *
 *******************************************************************/

#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <linux/wireless.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <net/if_arp.h>

#include "../config.h"
#include "../profile.h"
#include "linux/cardif_linux_ssids.h"
#include "cardif.h"
#include "../xsup_debug.h"
#include "../xsup_err.h"
#include "linux/cardif_linux.h"

#ifndef ETH_P_EAPOL
#define ETH_P_EAPOL 0x888e
#endif

#define SCAN_TIMEOUT  30   // Wait 30 seconds before scanning again.

// Define this, so the compiler doesn't complain.
extern unsigned int if_nametoindex(const char *);

/***********************************************
 *
 * Do whatever is needed to get the interface in to a state that we can send
 * and recieve frames on the network.  Any information that we need to later
 * use should be stored in the interface_data structure.
 *
 ***********************************************/
int cardif_init(struct interface_data *thisint)
{
  struct ifreq ifr;
  struct lin_sock_data *sockData;
  int sockopts, sockerr, retval;

  debug_printf(DEBUG_INT, "Initializing socket for interface %s..\n",
	       thisint->intName);

  // Find out what the interface index is.
  thisint->intIndex = if_nametoindex(thisint->intName);
  debug_printf(DEBUG_INT, "Index : %d\n", thisint->intIndex);

  // Allocate memory for the things we need.
  thisint->sockData = (void *)malloc(sizeof(struct lin_sock_data));
  if (thisint->sockData == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Error allocating memory!\n");
      return XEMALLOC;
    }

  sockData= thisint->sockData;

  // Amount of time to wait before allowing a scan again.
  sockData->scan_timer = 0;

  // Establish a socket handle.
  sockData->sockInt = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_EAPOL));
  if (sockData->sockInt < 0)
    {
      debug_printf(DEBUG_NORMAL, 
		   "Couldn't initialize raw socket for interface %s!\n",
		   thisint->intName);
      return XENOSOCK;
    }        

  // Tell the ifreq struct which interface we want to use.
  strncpy((char *)&ifr.ifr_name, thisint->intName, sizeof(ifr.ifr_name));

  retval = ioctl(sockData->sockInt, SIOCGIFINDEX, &ifr);
  if (retval < 0)
    {
      debug_printf(DEBUG_NORMAL, "Error getting interface index value for interface %s\n",
		   thisint->intName);
      return XESOCKOP;
    }

  // Build our link layer socket struct, so we can bind it to a specific
  // interface.
  sockData->sll.sll_family = PF_PACKET;
  sockData->sll.sll_ifindex = ifr.ifr_ifindex;
  sockData->sll.sll_protocol = htons(ETH_P_EAPOL);

  // Bind to the interface.
  retval = bind(sockData->sockInt, (const struct sockaddr *)&sockData->sll, 
		sizeof(struct sockaddr_ll));
  if (retval < 0)
    {
      debug_printf(DEBUG_NORMAL, "Error binding raw socket to interface %s!\n",
		   thisint->intName);
      return XESOCKOP;
    }

  // Get our MAC address.  (Needed for sending frames out correctly.)
  retval = ioctl(sockData->sockInt, SIOCGIFHWADDR, &ifr);
  if (retval < 0)
    {
      debug_printf(DEBUG_NORMAL, "Error getting hardware (MAC) address for interface %s!\n",
		   thisint->intName);
      return XENOTINT;
    }

  // Store a copy of our source MAC for later use.
  memcpy((char *)&thisint->source_mac[0], (char *)&ifr.ifr_hwaddr.sa_data[0], 6);

  // Check if we want ALLMULTI mode, and enable it.
  if (config_get_allmulti() == 1)
    {
      // Tell the ifreq struct which interface we want to use.
      strncpy((char *)&ifr.ifr_name, thisint->intName, sizeof(ifr.ifr_name));

      if (ioctl(sockData->sockInt, SIOCGIFFLAGS, &ifr) < 0)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't determine if ALLMULTI is enabled!\n");
	} else {
	  if (ifr.ifr_flags & IFF_ALLMULTI)
	    {
	      debug_printf(DEBUG_INT, "Allmulti mode is already enabled on this device!\n");
	      thisint->flags |= ALLMULTI;
	    } else {
	      debug_printf(DEBUG_INT, "Allmulti is currently disabled on this device!\n");
	      thisint->flags &= ~ALLMULTI;
	    }
	}

      ifr.ifr_flags |= IFF_ALLMULTI;
      if (ioctl(sockData->sockInt, SIOCSIFFLAGS, &ifr) < 0)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't set ALLMULTI mode on this interface!  We will continue anyway!\n");
	}
    }

  // Set our socket to non-blocking.
  sockopts = fcntl(sockData->sockInt, F_GETFL, 0);
  if (sockopts < 0)
    {
      debug_printf(DEBUG_NORMAL, "Error getting socket options for interface %s!\n",
		   thisint->intName);
      return XENOTINT;
    }

  sockerr = fcntl(sockData->sockInt, F_SETFL, sockopts | O_NONBLOCK);
  if (sockerr < 0)
    {
      debug_printf(DEBUG_NORMAL, "Error setting socket options for interface %s!\n",
		   thisint->intName);
      return XENOTINT;
    }



  return XENONE;
}

/****************************************************************
 *
 * This is called each time the internal clock ticks.  (Once a second.)
 * It should be used to update any timers that are to be used inside
 * of cardif.
 *
 ****************************************************************/
void cardif_clock_tick(struct interface_data *intdata)
{
  struct lin_sock_data *sockData;

  sockData= intdata->sockData;

  if (sockData->scan_timer > 0)
    {
      sockData->scan_timer--;
    }
}

/**************************************************************
 * 
 * Check if encryption is available.  If it is, we will return
 * TRUE, if it isn't, we will return FALSE.  On error, we return
 * -1.
 *
 **************************************************************/
int cardif_enc_enabled(struct interface_data *thisint)
{
  int rc = 0;
  int skfd;
  struct iwreq wrq;

  bzero((struct iwreq *)&wrq, sizeof(struct iwreq));

  skfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (skfd < 0)
    return -1;

  strncpy(wrq.ifr_name, thisint->intName, IFNAMSIZ);

  if ((rc = ioctl(skfd, SIOCGIWENCODE, &wrq)) < 0)
    {
      // We got an error while trying to get encryption information
      // from the card.
      rc = -1;
    } else {
      // We got some data, so see if we have encryption or not.
      if ((wrq.u.data.flags & IW_ENCODE_DISABLED) == IW_ENCODE_DISABLED)
	{
	  // Encryption is disabled.
	  rc = FALSE;
	} else {
	  // Encryption is enabled.
	  rc = TRUE;
	}
    }
 
  close(skfd);
  return rc;
}

/**************************************************************
 *
 * Force the card in to open mode.  (The call cardif_enc_open will only
 * set the card to open mode if it has been in the past.)
 *
 **************************************************************/
int cardif_force_enc_on_open(struct interface_data *thisint)
{
  int rc = 0;
  char key[5]={0x00,0x00,0x00,0x00,0x00};
  struct iwreq wrq;
  struct lin_sock_data *sockData;

  if (!(thisint->flags & WEP))
    {
      debug_printf(DEBUG_INT, "WEP not desired!\n");
      return XENONE;
    }

  sockData = thisint->sockData;
  bzero((struct iwreq *)&wrq, sizeof(struct iwreq));

  strncpy(wrq.ifr_name, thisint->intName, IFNAMSIZ);
  wrq.u.data.pointer = (caddr_t)&key;
  wrq.u.data.flags = 0;
  wrq.u.data.length = 5;

  if (ioctl(sockData->sockInt, SIOCSIWENCODE, &wrq) < 0)
    {
      debug_printf(DEBUG_INT, "Error setting open encoding mode!\n");
      rc = -1;
    }
 
  return rc;
}

/**************************************************************
 *
 * If we have detected, or forced this interface to reset keys, then
 * we need to reset them.  Otherwise, we will just ignore the fact that
 * we changed APs, and return.
 *
 **************************************************************/
void cardif_reset_keys(struct interface_data *thisint)
{
  char zerokey[13];
  char keylen = 13;
#ifdef EXPERIMENTAL
  struct ssids_list *ourssid;
#endif

  if (thisint->userdata == NULL) 
    {
      debug_printf(DEBUG_INT, "Userdata is NULL!\n");
      return;
    }

  if (thisint->userdata->wireless_ctrl == CTL_NO) 
    {
      debug_printf(DEBUG_INT, "Config file has instructed us not to reset the key!  Roaming may not work!!!\n");
      return;
    }

#ifdef EXPERIMENTAL
  ourssid = cardif_linux_ssids_find(thisint);

  if (ourssid == NULL)
    {
      // Leave this as a debug INT because it is more informational, and in 
      // the event we have an invalid SSID for a long time the log will fill
      // up with these messages...  Not pretty!
      debug_printf(DEBUG_INT, "Couldn't find any information about the SSID we are using!\n");
      return;
    }

  if (!(ourssid->flags & WEP)) 
    {
      debug_printf(DEBUG_INT, "SSID doesn't appear to use encryption!  Ignoring!\n");
      return;
    }
#else
    if (cardif_enc_enabled(thisint) != TRUE)
    {
      debug_printf(DEBUG_INT, "Encryption appears to be disabled.  We will not reset keys on interface %s!\n", thisint->intName);
      return;
    }
#endif

  bzero(&zerokey, 13);

  // We set the key index to 0x80, to force key 0 to be set to all 0s,
  // and to have key 0 be set as the default transmit key.
  set_wireless_key(thisint, (char *)&zerokey, keylen, 0x80);
  set_wireless_key(thisint, (char *)&zerokey, keylen, 0x01);
  set_wireless_key(thisint, (char *)&zerokey, keylen, 0x02);
  set_wireless_key(thisint, (char *)&zerokey, keylen, 0x03);
}

/**************************************************************
 *
 * Tell the wireless card to start scanning for wireless networks.
 *
 **************************************************************/
int cardif_start_wireless_scan(struct interface_data *thisint)
{
#ifdef EXPERIMENTAL
  struct lin_sock_data *sockData;
  struct iwreq iwr;

  if (!(thisint->flags & IS_WIRELESS))
    {
      debug_printf(DEBUG_INT, "%s is not a wireless interface!\n", 
		   thisint->intName);
      return XENOWIRELESS;
    }

  if (thisint->flags & SCANNING)
    {
      debug_printf(DEBUG_INT, "Already scanning!\n");
      return XENONE;
    }

  sockData = thisint->sockData;

  if (sockData->scan_timer > 0)
    {
      debug_printf(DEBUG_INT, "Scan timer has not expired!  Not scanning!\n");
      return XENONE;
    }

  debug_printf(DEBUG_INT, "Issuing scan request for interface %s!\n",
	       thisint->intName);

  // Clear out our active list of SSIDs.
  cardif_linux_ssids_clear(thisint);

  iwr.u.param.flags = IW_SCAN_DEFAULT;
  iwr.u.param.value = 0;
  strcpy((char *)&iwr.ifr_name, thisint->intName);

  if (ioctl(sockData->sockInt, SIOCSIWSCAN, &iwr) < 0)
    {
      debug_printf(DEBUG_NORMAL, "Error with SCAN ioctl!  (Perhaps your card doesn't support scanning?)\n");
      return -1;
    }
  debug_printf(DEBUG_EVERYTHING, "Flags before setting scan flag : %02X\n",
	       thisint->flags);

  SET_FLAG(thisint->flags, SCANNING);  // We are scanning.

  debug_printf(DEBUG_EVERYTHING, "Flags after setting scan flag : %02X\n",
	       thisint->flags);

  sockData->scan_timer = SCAN_TIMEOUT;
#endif
  return XENONE;
}

/**************************************************************
 *
 * Check to see if the BSSID value is valid.  If it is, return TRUE. If
 * it isn't return FALSE.
 *
 **************************************************************/
int cardif_valid_dest(struct interface_data *thisint)
{
  char baddest[6], newdest[6];

  if ((thisint->flags & IS_WIRELESS) && 
      (GetBSSID(thisint, (char *)&newdest) == XENONE))
    {
      memset((char *)&baddest, 0x00, 6);
      if (memcmp(thisint->dest_mac, baddest, 6) == 0)
	{
	  debug_printf(DEBUG_INT, "All 0s for dest mac!\n");
#ifdef EXPERIMENTAL
	  cardif_start_wireless_scan(thisint);
#endif
	  return FALSE;

	}

      memset((char *)&baddest, 0x44, 6);
      if (memcmp(thisint->dest_mac, baddest, 6) == 0)
	{
	  debug_printf(DEBUG_INT, "All 4s for dest mac!\n");
#ifdef EXPERIMENTAL
	  cardif_start_wireless_scan(thisint);
#endif
	  return FALSE;
	}

      memset((char *)&baddest, 0xff, 6);
      if (memcmp(thisint->dest_mac, baddest, 6) == 0)
	{
	  debug_printf(DEBUG_INT, "All Fs for dest mac!\n");
#ifdef EXPERIMENTAL
	  cardif_start_wireless_scan(thisint);
#endif
	  return FALSE;
	}
    }
  
  return TRUE;
}

/**************************************************************
 *
 * If we determine that this interface is a wireless interface, then
 * we should call this, to have the destination address changed to the
 * AP that we are talking to.  Otherwise, we will always send frames to
 * the multicast address, instead of the AP.  (And, most APs won't answer
 * to the multicast address.)
 *
 **************************************************************/
int cardif_check_dest(struct interface_data *thisint)
{
  char newdest[6], *newssid;
  int changed = FALSE;

  bzero((char *)&newdest, 6);

  // If we are on wireless, figure out the target MAC address.
  if ((thisint->flags & IS_WIRELESS) && 
      (GetBSSID(thisint, (char *)&newdest) == XENONE))
    {
      if (memcmp(thisint->dest_mac, newdest, 6) != 0)
	{
	  debug_printf(DEBUG_INT, "The card reported that the destination MAC address is now ");
	  debug_hex_printf(DEBUG_INT, (char *)&newdest, 6);

	  memcpy((char *)&thisint->dest_mac[0], (char *)&newdest, 6);

	  changed = TRUE;

	  // Since we changed destination addresses, we need to see if
	  // we should reset keys.
	  cardif_reset_keys(thisint);
	}

      if (cardif_valid_dest(thisint) == FALSE)
	{
	  cardif_reset_keys(thisint);
	  if ((!(thisint->flags & SCANNING)) && 
	      (!(thisint->flags & SCAN_DONE))) 
	    cardif_start_wireless_scan(thisint);

	  if (thisint->flags & SCAN_DONE)
	    cardif_linux_ssids_join_network(thisint);
	}

      // If we were able to get a BSSID, we should also try to get an SSID.
      newssid = malloc(100);
      if (newssid == NULL)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't malloc newssid in cardif_linux.\n");
	  return XEMALLOC;
	}

      bzero(newssid, 100);
      GetSSID(thisint, newssid);
      if ((thisint->cur_essid == NULL) ||
	  (strncmp(newssid, thisint->cur_essid, 100) != 0))
	{
	  if (thisint->cur_essid != NULL) free(thisint->cur_essid);
	  thisint->cur_essid = newssid;
	  debug_printf(DEBUG_INT, "Working with ESSID : %s\n",
		       thisint->cur_essid);
	} else {
	  if (newssid != NULL)
	    {
	      free(newssid);
	      newssid = NULL;
	    }
	}
    } 

  return changed;
}

/******************************************
 *
 * Return the socket number for functions that need it.
 *
 ******************************************/
int cardif_get_socket(struct interface_data *thisint)
{
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  return sockData->sockInt;
}

/******************************************
 *
 * Clean up anything that was created during the initialization and operation
 * of the interface.  This will be called before the program terminates.
 *
 ******************************************/
int cardif_deinit(struct interface_data *thisint)
{
  struct ifreq ifr;
  uint16_t int16;
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  debug_printf(DEBUG_EVERYTHING, "Cleaning up interface %s...\n",thisint->intName);

  // Check if we want ALLMULTI mode, and enable it.
  if (config_get_allmulti() == 1)
    {
      // Tell the ifreq struct which interface we want to use.
      strncpy((char *)&ifr.ifr_name, thisint->intName, sizeof(ifr.ifr_name));

      if (ioctl(sockData->sockInt, SIOCGIFFLAGS, &ifr) < 0)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't get interface flags!\n");
	} else {
	  // Check if allmulti was disabled when we started.  If it was,
	  // then disable it again, so everything is good.
	  if (!(thisint->flags & ALLMULTI))
	    {
	      debug_printf(DEBUG_INT, "Turning off ALLMULTI mode!\n");

	      int16 = ifr.ifr_flags;

	      // ANDing the flags with 0xfdff will turn off the ALLMULTI flag.
	      ifr.ifr_flags = (int16 & 0xfdff);
	      if (ioctl(sockData->sockInt, SIOCSIFFLAGS, &ifr) < 0)
		{
		  debug_printf(DEBUG_NORMAL, "Couldn't set ALLMULTI mode on this interface!  We will continue anyway!\n");
		}
	    }
	}
    }

  close(sockData->sockInt);

  // Now clean up the memory.
  if (thisint->sockData != NULL)
    {
      free(thisint->sockData);
      thisint->sockData = NULL;
    }

  return XENONE;
}

/******************************************
 *
 * Set a wireless key.  Also, based on the index, we may change the transmit
 * key.
 *
 ******************************************/
int set_wireless_key(struct interface_data *thisint, u_char *key, int keylen,
		     int index)
{
  int rc = 0;
  int skfd;
  struct iwreq wrq;

  if (!(thisint->flags & IS_WIRELESS))
    {
      if ((cardif_int_is_wireless(thisint->intName) != TRUE) ||
	  (thisint->userdata->type == WIRED) ||
	  (thisint->userdata->wireless_ctrl == CTL_NO))
	{
	  debug_printf(DEBUG_NORMAL, "Interface isn't wireless, but an attempt to set a key was made!\n");
	  return XENOWIRELESS;
	} else {
	  thisint->flags |= IS_WIRELESS;
	}
    }

  skfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (skfd < 0)
    return -1;

  strncpy(wrq.ifr_name, thisint->intName, IFNAMSIZ);

  wrq.u.data.flags = ((index & 0x7f) + 1) & IW_ENCODE_INDEX;
  wrq.u.data.flags |= IW_ENCODE_OPEN;

  wrq.u.data.length = keylen;
  wrq.u.data.pointer = (caddr_t)key;

  if ((rc = ioctl(skfd, SIOCSIWENCODE, &wrq)) < 0)
    {
      debug_printf(DEBUG_NORMAL, "Failed to set WEP key [%d], error %d : %s\n",
		   (index & 0x7f) + 1, errno, strerror(errno));

      rc = XENOKEYSUPPORT;
    } else {
      debug_printf(DEBUG_INT, "Successfully set WEP key [%d]\n",
		   (index & 0x7f)+1);

      if (index & 0x80)
	{
	  // This is a unicast key, use it for transmissions.
	  strncpy(wrq.ifr_name, thisint->intName, IFNAMSIZ);

	  wrq.u.data.flags = (((index & 0x7f) + 1) & IW_ENCODE_INDEX) | IW_ENCODE_NOKEY;
	  wrq.u.data.flags |= IW_ENCODE_OPEN;

	  wrq.u.data.length = 0;
	  wrq.u.data.pointer = (caddr_t)NULL;

	  if (ioctl(skfd, SIOCSIWENCODE, &wrq) < 0)
	    {
	      debug_printf(DEBUG_NORMAL, "Failed to set the WEP transmit key ID [%d]\n", (index & 0x7f)+1);
	      rc = XENOKEYSUPPORT;
	    } else {
	      debug_printf(DEBUG_INT, "Successfully set the WEP transmit key [%d]\n", (index & 0x7f)+1);
	    }
	}  
    }
 
  close(skfd);
  return rc;
}

/**********************************************************
 *
 * Set the SSID of the wireless card.
 *
 **********************************************************/
int SetSSID(struct interface_data *thisint, char *ssid_name)
{
  struct iwreq iwr;
  struct lin_sock_data *sockData;

  if (thisint == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Invalid interface struct passed in to %s!\n",
		   __FUNCTION__);
      return XEGENERROR;
    }

  sockData = thisint->sockData;
 
  if (!(thisint->flags & IS_WIRELESS))
    {
      // We want to verify that the interface is in fact, not wireless, and
      // not that we are in a situation where the interface has just been 
      // down.
      if (!(thisint->flags & WAS_DOWN))
	{
	  return XENOWIRELESS;
	}
    } 

  // If we get here, and isWireless == FALSE, then we need to double
  // check that our interface is really not wireless.
  if (!(thisint->flags & IS_WIRELESS))
    {
      if (cardif_int_is_wireless(thisint->intName) == TRUE)
	{
	  thisint->flags |= IS_WIRELESS;
	} else {
	  thisint->flags &= (~IS_WIRELESS);
	}

      if (!(thisint->flags & IS_WIRELESS))
	{
	  thisint->flags &= (~WAS_DOWN);
	}
    }

  // Specify the interface name we are asking about.
  strncpy(iwr.ifr_name, thisint->intName, sizeof(iwr.ifr_name));

  iwr.u.essid.pointer = (caddr_t) ssid_name;
  iwr.u.essid.length = strlen(ssid_name);
  iwr.u.essid.flags = 1;

  if (ioctl(sockData->sockInt, SIOCSIWESSID, &iwr) < 0) return XENOWIRELESS;

  thisint->flags &= (~WAS_DOWN);

  return XENONE;
}

/******************************************
 *
 * Do whatever we need to do in order to associate based on the flags in
 * the ssids_list struct.
 *
 ******************************************/
void cardif_associate(struct interface_data *intdata, void *newssid)
{
#ifdef EXPERIMENTAL
  struct ssids_list *ssid;

  ssid = (struct ssids_list *)newssid;

  if (intdata == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Invalid interface struct passed to %s!\n",
		   __FUNCTION__);
      return;
    }

  if (ssid == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Invalid SSID information passed to %s!\n",
		   __FUNCTION__);
      return;
    }

  debug_printf(DEBUG_INT, "Setting SSID to %s\n", ssid->ssidName);

  if (ssid->flags & WEP)
    {
      debug_printf(DEBUG_INT, "Setting open encryption.\n");
      cardif_force_enc_on_open(intdata);
    }

  if (ssid->flags & WPA_IE)
    {
      debug_printf(DEBUG_INT, "Enabling WPA!\n");
      cardif_linux_wpa_set_wpa_ie(intdata, NULL, 0);
      cardif_linux_wpa_enable(intdata);
    }

  // Otherwise, set the SSID.
  if (SetSSID(intdata, ssid->ssidName) != XENONE)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't associate to %s!\n", ssid->ssidName);
      return;
    }
#endif
}

/******************************************
 *
 * Ask the wireless card for the ESSID that we are currently connected to.  If
 * this is not a wireless card, or the information is not available, we should
 * return an error.
 *
 ******************************************/
int GetSSID(struct interface_data *thisint, char *ssid_name)
{
  struct iwreq iwr;
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;
 
  if (!(thisint->flags & IS_WIRELESS))
    {
      // We want to verify that the interface is in fact, not wireless, and
      // not that we are in a situation where the interface has just been 
      // down.
      if (!(thisint->flags & WAS_DOWN))
	{
	  return XENOWIRELESS;
	}
    } 

  // If we get here, and isWireless == FALSE, then we need to double
  // check that our interface is really not wireless.
  if (!(thisint->flags & IS_WIRELESS))
    {
      if (cardif_int_is_wireless(thisint->intName) == TRUE)
	{
	  thisint->flags |= IS_WIRELESS;
	} else {
	  thisint->flags &= (~IS_WIRELESS);
	}

      if (!(thisint->flags & IS_WIRELESS))
	{
	  thisint->flags &= (~WAS_DOWN);
	}
    }

  // Specify the interface name we are asking about.
  strncpy(iwr.ifr_name, thisint->intName, sizeof(iwr.ifr_name));

  iwr.u.essid.pointer = (caddr_t) ssid_name;
  iwr.u.essid.length = 100;
  iwr.u.essid.flags = 0;

  if (ioctl(sockData->sockInt, SIOCGIWESSID, &iwr) < 0) return XENOWIRELESS;

  thisint->flags &= (~WAS_DOWN);

  return XENONE;
}

/******************************************
 *
 * Check the SSID against what we currently have, and determine if we need
 * to reset our configuration.
 *
 ******************************************/
int cardif_check_ssid(struct interface_data *thisint)
{
  char new_essid[100];

  bzero((char *)&new_essid, 100);

  if (GetSSID(thisint, (char *)&new_essid) != XENONE)
    {
      // This interface probably isn't wireless!

      // On the off chance that it is, we will trash the essid we have
      // listed as the current one, so that if we suddenly do get an
      // essid, we will load the proper config.
      if (thisint->cur_essid != NULL)
	{
	  free(thisint->cur_essid);
	  thisint->cur_essid = NULL;
	}

      return XENONE;
    }

  if (thisint->cur_essid != NULL)
    {
      if (strcmp(thisint->cur_essid, (char *)&new_essid) != 0)
	{
	  // We have changed essids.
	  debug_printf(DEBUG_INT, "ESSID Changed to : %s\n", (char *)&new_essid);
	  
	  // Kill off the essid we currently have.
	  free(thisint->cur_essid);
	  thisint->cur_essid = (char *)malloc(strlen(new_essid)+1);
	  if (thisint->cur_essid == NULL) return XEMALLOC;
	  
	  strncpy(thisint->cur_essid, new_essid, strlen(new_essid));

	  // Since we changed essids, we no longer have completed a
	  // "first auth"
	  thisint->flags &= (~FIRSTAUTH);
	  //	  thisint->firstauth = FALSE;

	  return XNEWESSID;
	}
    }
  return XENONE;
}

/******************************************
 *
 * Get the Broadcast SSID (MAC address) of the Access Point we are connected 
 * to.  If this is not a wireless card, or the information is not available,
 * we should return an error.
 *
 ******************************************/
int GetBSSID(struct interface_data *thisint, char *bssid_dest)
{
  struct iwreq iwr;
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  // Specify the interface name we are asking about.
  strncpy(iwr.ifr_name, thisint->intName, sizeof(iwr.ifr_name));
  
  if (ioctl(sockData->sockInt, SIOCGIWAP, &iwr) < 0) return XENOWIRELESS;

  memcpy(bssid_dest, iwr.u.ap_addr.sa_data, 6);
  return XENONE;
}

/******************************************
 *
 * Set the flag in the state machine that indicates if this interface is up
 * or down.  If there isn't an interface, we should return an error.
 *
 ******************************************/
int get_if_state(struct interface_data *thisint)
{
  int retVal;
  struct ifreq ifr;
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  strncpy(ifr.ifr_name, thisint->intName, sizeof(ifr.ifr_name));
  retVal = ioctl(sockData->sockInt, SIOCGIFFLAGS, &ifr);
  if (retVal < 0)
    {
      debug_printf(DEBUG_NORMAL, "Interface %s not found!\n", thisint->intName);
      return FALSE;
    }
  
  if ((ifr.ifr_flags & IFF_UP) == IFF_UP)
    {
      return TRUE;
    } else {
      thisint->flags |= WAS_DOWN;
      return FALSE;
    }
  return XENONE;
}

/******************************************
 *
 * Send a frame out of the network card interface.  If there isn't an 
 * interface, we should return an error.  We should return a different error
 * if we have a problem sending the frame.
 *
 ******************************************/
int sendframe(struct interface_data *thisint, char *sendframe, int sendsize)
{
  char nomac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  int retval;
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  if (thisint == NULL) return XEMALLOC;

  if (sendframe == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Cannot send NULL frame!\n");
      return XENOFRAMES;
    }

  // The frame we are handed in shouldn't have a src/dest, so put it in.
  memcpy(&sendframe[0], &thisint->dest_mac[0], 6);
  memcpy(&sendframe[6], &thisint->source_mac[0], 6);

  if (thisint->userdata != NULL)
    {
      if (memcmp(nomac, (char *)&thisint->userdata->dest_mac[0], 6) != 0)
	{
	  debug_printf(DEBUG_INT, "Static MAC address defined!  Using it!\n");
	  memcpy(&sendframe[0], &thisint->userdata->dest_mac[0], 6);
	}
    }

  debug_printf(DEBUG_EVERYTHING, "Frame to be sent : \n");
  debug_hex_dump(DEBUG_EVERYTHING, sendframe, sendsize);

  retval = sendto(sockData->sockInt, sendframe, sendsize, 0,
		  (struct sockaddr *)&sockData->sll, sizeof(sockData->sll));
  if (retval <= 0)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't send frame! (%s)\n", strerror(errno));
    }

  return retval;
}

/******************************************
 * 
 * Get a frame from the network.  Since we are in promisc. mode, we will get
 * frames that aren't intended for us.  So, check the frame, determine if it
 * is something we care about, and act accordingly.
 *
 ******************************************/
int getframe(struct interface_data *thisint, char *resultframe, int *resultsize)
{
  int newsize=0;
  char dot1x_default_dest[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  errno = 0;
  *resultsize = 1550;

  newsize = recvfrom(sockData->sockInt, resultframe, *resultsize, 0, 0, 0);
  if (newsize <= 0)
    {
      debug_printf(DEBUG_EXCESSIVE, "Couldn't get frame.  (Maybe there weren't any!)\n");
      switch (errno)
	{
	case EBADF:
	  debug_printf(DEBUG_EXCESSIVE, "Invalid descriptor!\n");
	  break;
	case ECONNREFUSED:
	  debug_printf(DEBUG_EXCESSIVE, "Connection refused!\n");
	  break;
	case ENOTCONN:
	  debug_printf(DEBUG_EXCESSIVE, "Not connected!\n");
	  break;
	case ENOTSOCK:
	  debug_printf(DEBUG_EXCESSIVE, "Not a socket!\n");
	  break;
	case EAGAIN:
	  debug_printf(DEBUG_EXCESSIVE, "Socket would block!\n");
	  break;
	case EINTR:
	  debug_printf(DEBUG_EXCESSIVE, "Recieve Interrupted!\n");
	  break;
	case EFAULT:
	  debug_printf(DEBUG_EXCESSIVE, "Invalid recieve buffer!\n");
	  break;
	case EINVAL:
	  debug_printf(DEBUG_EXCESSIVE, "Invalid argument!\n");
	  break;
	case ENOMEM:
	  debug_printf(DEBUG_EXCESSIVE, "Couldn't allocate memory!\n");
	  break;
	default:
	  debug_printf(DEBUG_EVERYTHING, "Unknown error (%d)\n",newsize);
	  break;
	}
      return XENOFRAMES;
    } else {
      debug_printf(DEBUG_EVERYTHING, "Got Frame : \n");
      debug_hex_dump(DEBUG_EVERYTHING, resultframe, newsize);
    }

  // Make sure that the frame we got is for us..
  if ((memcmp(&thisint->source_mac[0], &resultframe[0], 6) == 0) ||
      ((memcmp(&resultframe[0], &dot1x_default_dest[0], 6) == 0) &&
       (memcmp(&resultframe[6], &thisint->source_mac[0], 6) != 0)))
    {
      *resultsize = newsize;
      return newsize;
    }

  // Otherwise it isn't for us. 
  debug_printf(DEBUG_INT, "Got a frame, not for us.\n");
  return XENOFRAMES;
}

/******************************************
 * 
 * Return true if there is a frame in the queue to be processed.
 *
 ******************************************/
int frameavail(struct interface_data *thisint)
{
  int newsize=0;
  char resultframe[1520];
  struct lin_sock_data *sockData;

  sockData = thisint->sockData;

  newsize = recvfrom(sockData->sockInt, &resultframe, 1520, MSG_PEEK, 0, 0);
  if (newsize > 0) return TRUE;

  return FALSE;
}

/******************************************
 *
 * Validate an interface, based on if it has a MAC address.
 *
 ******************************************/
int cardif_validate(char *interface)
{
  int sd, res;
  struct ifreq ifr;

  strncpy(ifr.ifr_name, interface, sizeof(interface)+1);

  sd = socket(PF_PACKET, SOCK_RAW, 0);
  if (sd < 0)
    return FALSE;
  res = ioctl(sd, SIOCGIFHWADDR, &ifr);
  close(sd);
  if (res < 0)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't get information for interface %s!\n",interface);
    } else {
      switch (ifr.ifr_hwaddr.sa_family)
	{
	case ARPHRD_ETHER:
	case ARPHRD_IEEE80211:
	  return TRUE;
	}
    }
  return FALSE;
}

/******************************************
 *
 * Get the name of an interface, based on an index value.
 *
 ******************************************/
#define PROC_DEV_FILE  "/proc/net/dev"

int cardif_get_int(int index, char *retInterface)
{
  FILE *fp;
  int hits;
  char line[1000], *lineptr;

  hits = 0;

  fp = fopen(PROC_DEV_FILE, "r");
  if (fp == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't access /proc/net/dev!\n");
      exit(250);
    }

  bzero(line, 1000);

  while ((hits <= index) && (fgets(line, 999, fp) != NULL))
    { 
      lineptr = strchr(line, ':');
      
      if (lineptr == NULL) continue;

      *lineptr = '\0';
      lineptr = &line[0];

      while (*lineptr == ' ') lineptr++;  // Strip out blanks.
      
      strcpy(retInterface, lineptr);
      hits++;
    }

  if (hits <= index)
    {
      debug_printf(DEBUG_INT, "No more interfaces to look at!\n");
      return XNOMOREINTS;
    }

  debug_printf(DEBUG_INT, "Found interface : %s\n",retInterface);

  fclose(fp);

  return XENONE;   // No errors.
}


/*******************************************************
 *
 * Check to see if an interface is wireless.  On linux, we look in
 * /proc/net/wireless to see if the interface is registered with the
 * wireless extensions.
 *
 *******************************************************/
#define PROC_WIRELESS_FILE  "/proc/net/wireless"

int cardif_int_is_wireless(char *interface)
{
  FILE *fp;
  char line[1000], *lineptr=NULL;
  int done;

  done = FALSE;

  fp = fopen(PROC_WIRELESS_FILE, "r");
  if (fp == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't access /proc/net/wireless!  (You probably don't have wireless extensions enabled!)\n");
      return -1;
    }

  bzero(line, 1000);

  while ((!done) && (fgets(line, 999, fp) != NULL))
    { 
      lineptr = strchr(line, ':');
      
      if (lineptr != NULL)
	{
	  
	  *lineptr = '\0';
	  lineptr = &line[0];
	  
	  while (*lineptr == ' ') lineptr++;  // Strip out blanks.
	  if (lineptr != NULL)
	    {
	      if (strcmp(lineptr, interface) == 0) done=TRUE;
	    }
	}
    }
  fclose(fp);
  
  if ((lineptr != NULL) && (strcmp(lineptr, interface) == 0))
    {
      debug_printf(DEBUG_INT, "Interface %s is wireless!\n",interface);
      return TRUE;
    } else {
      debug_printf(DEBUG_INT, "Interface %s is NOT wireless!\n",interface);
      return FALSE;
    }
  return XENONE;   // No errors.
}
