normal socket ioctl to net interface ioctl

Write down for wifi framework.

Describe how the flow is transaferred from a normal socket to the specific network interface ioctl. 

 

An artical from Vipul Gupta

 

In general an ioctl call in a user program looks like ioctl(int fd, int command, (char *) argstruct). For ioctl calls related to the networking code (these are the only ones we will deal with in this note), the file descriptor fd is actually a socket descriptor returned by the socket() system call. The command could be any one of those listed in /usr/include/linux/sockios.h. These commands are subdivided into a number of categories depending on what aspect of networking they deal with:

 

changing the routing table (e.g. SIOCADDRT, SIOCDELRT),

reading/updating the ARP/RARP caches (e.g. SIOCDARP, SIOCSRARP),

generic functions related to network interfaces (e.g. SIOCGIFNAME, SIOCSIFADDR etc)

The Goodies directory contains a number of sample programs illustrating the use of networking ioctl calls. As you look at these programs, notice how the structure used for argstruct depends on the ioctl command type. For example, routing table related ioctls use the rtentry structure defined in /usr/include/linux/route.h (see adddefault.c for an example) and ARP related ioctls use the arpreq structure defined in /usr/include/linux/if_arp.h (see arpread.c).

 

Network interface related ioctl commands typically look like SIOCxIFyyyy where x is either S (set, write) or G (get, read). The getifinfo.c program uses such commands to read the IP address, hardware address, broadcast address and flags associated with a network interface. For these ioctls, the third argument is an ifreq structure which is defined in /usr/include/linux/if.h. In some cases, new ioctl commands may be needed in addition to those defined in sockios.h, e.g. the WaveLAN wireless networking card maintains information about wireless signal strength which may be of use to a user program. How should user programs be allowed access to this information? Our first instinct may be to define a new ioctl command in sockios.h, e.g. SIOCGIFWVLNSS (Get WaVeLaN Signal Strength). Unfortunately, this command makes no sense at all for other interfaces (e.g. loopback) and attempts to use this ioctl command on interfaces other than WaveLAN cards should be disallowed. What we need, then, is a mechanism to define interface specific ioctl commands. Luckily, the Linux OS already has built-in hooks for this purpose. If you look at sockios.h again, you will notice that each device has a predefined SIOCDEVPRIVATE ioctl command. The implementation of this command is left totally upto the person writing the corresponding device driver.

 

By convention, a user program invokes a device specific ioctl command as ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr) where ifr is defined as struct ifreq ifr. It fills ifr.ifr_name with the interface name associated with the device, e.g. on tether, the WaveLAN card is named eth1. Typically, a user program will also need to exchange command arguments and results with the kernel and that is done through the ifr.ifr_data field, e.g. the signal strength infromation for the WaveLAN card could be returned in this field. The Linux source code already includes two devices de4x5 and ewrk3 that define and implement device specific ioctl commands. The source code for these drivers is in de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, (in /usr/src/linux/drivers/net/). Both drivers define their own private structures (struct ewrk3_ioctl and struct de4x5_ioctl) for exchanging information between user programs and the drivers. Before the ioctl call, the user program fills out the necessary fields in this structure and points ifr.ifr_data to it.

 

Before we go any further into the driver code for ewrk3 and de4x5, let us trace through various steps in the processing of an ioctl call. All interface-type ioctl requests (SIOCxIFyyyy and SIOCDEVPRIVATE) result in dev_ioctl() (in /usr/src/linux/net/core/dev.c) being called. This is just a wrapper and most of the real action is left for dev_ifsioc() (also in dev.c). About the only thing dev_ioctl() does is check whether a calling process has the appropriate permissions to issue the command (e.g. commands to alter the routing tables require root permissions). One of the first things dev_ifsioc() does is get the device structre (struct device defined in /usr/include/linux/netdevice.h) corresponding to the device named in ifr.ifr_name. This is followed by the code to implement generic interface commands (e.g. SIOCGIFADDR) inside a giant switch statement. The SIOCDEVPRIVATE command and any others with codes from 0x89F0 through 0x89FF end up in the default: branch for this switch. Here, the kernel checks to see if a device specific ioctl handler has been set up in the device structure. The handler is maintained as a function pointer in the do_ioctl field of the device structure. If the handler has been set, the kernel invokes it.

 

So, to implement device specific ioctls, all one needs to do is write a device specific ioctl handler and have the do_ioctl field in the corresponding device structure point to it. For the ewrk3 device, this function is called ewrk3_ioctl() (in ewrk3.c) and the corresponding device structure is initialized in ewrk3_init(). The ewrk3_ioctl() code clearly indicates the use of ifr.ifr_data for exchanging information between the device driver and user program. Note that this area of memory can be used for bidirectional information exchange. For example, in the ewrk3 driver code, the first two bytes of ifr.ifr_data are used to convey the specific action (e.g. EWRK3_SET_PROM, EWRK3_CLR_PROM) desired by the user (the driver implements multiple device specific commands that are all invoked through SIOCDEVPRIVATE). Also, and the buffer pointed to by the fifth byte in ifr.ifr_data is used to exchange other information (hardware address when using EWRK3_SET_HWADDR or EWRK3_GET_HWADDR).

 

As you go through ewrk3_ioctl(), keep in mind that normally a user process cannot directly access kernel memory. For this reason, two special procedures memcpy_tofs() and memcpy_fromfs() are provided to driver writers. The kernel procedure memcpy_tofs(arg1, arg2, arg3) copies arg3 bytes from starting address arg2 (in kernel space) to address arg1 (in user space). Similarly, memcpy_fromfs(arg1, arg2, arg3) copies arg3 bytes from starting address arg2 (in user space) to address arg1 (in kernel space). These procedures are preceded by calls to verify_area() to verify that the process has appropriate access permissions. Also notice the use of the printk() function to print debugging information. This function is similar to printf() but cannot handle floating point. The printf() function is not available to kernel code. The output generated by printk() is logged into /usr/adm/messages. For more information on these and related procedures, look at the section titled Supporting Functions in Michael K. Johnson's "Linux Kernel Hacker's Guide" which is accessible from the Linux Documentation Home Page.

 

Let me paste the code extracted from kernel 2.6.xx. 

asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)

{

      struct file * filp;

      int error = -EBADF;

      int fput_needed;

 

      filp = fget_light(fd, &fput_needed);

      if (!filp)

            goto out;

 

      error = security_file_ioctl(filp, cmd, arg);

      if (error)

            goto out_fput;

 

      error = vfs_ioctl(filp, fd, cmd, arg);

 out_fput:

      fput_light(filp, fput_needed);

 out:

      return error;

}

 

 

int vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg)

{

      unsigned int flag;

      int on, error = 0;

 

      switch (cmd) {

            case FIOCLEX:

                  set_close_on_exec(fd, 1);

                  break;

 

            case FIONCLEX:

                  set_close_on_exec(fd, 0);

                  break;

 

            case FIONBIO:

                  if ((error = get_user(on, (int __user *)arg)) != 0)

                        break;

                  flag = O_NONBLOCK;

#ifdef __sparc__

                  /* SunOS compatibility item. */

                  if(O_NONBLOCK != O_NDELAY)

                        flag |= O_NDELAY;

#endif

                  if (on)

                        filp->f_flags |= flag;

                  else

                        filp->f_flags &= ~flag;

                  break;

 

            case FIOASYNC:

                  if ((error = get_user(on, (int __user *)arg)) != 0)

                        break;

                  flag = on ? FASYNC : 0;

 

                  /* Did FASYNC state change ? */

                  if ((flag ^ filp->f_flags) & FASYNC) {

                        if (filp->f_op && filp->f_op->fasync) {

                              lock_kernel();

                              error = filp->f_op->fasync(fd, filp, on);

                              unlock_kernel();

                        }

                        else error = -ENOTTY;

                  }

                  if (error != 0)

                        break;

 

                  if (on)

                        filp->f_flags |= FASYNC;

                  else

                        filp->f_flags &= ~FASYNC;

                  break;

 

            case FIOQSIZE:

                  if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) ||

                      S_ISREG(filp->f_path.dentry->d_inode->i_mode) ||

                      S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) {

                        loff_t res = inode_get_bytes(filp->f_path.dentry->d_inode);

                        error = copy_to_user((loff_t __user *)arg, &res, sizeof(res)) ? -EFAULT : 0;

                  }

                  else

                        error = -ENOTTY;

                  break;

            default:

                  if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))

                        error = file_ioctl(filp, cmd, arg);

                  else

                        error = do_ioctl(filp, cmd, arg);

                  break;

      }

      return error;

}

 

static int file_ioctl(struct file *filp, unsigned int cmd,

            unsigned long arg)

{

      int error;

      int block;

      struct inode * inode = filp->f_path.dentry->d_inode;

      int __user *p = (int __user *)arg;

 

      switch (cmd) {

            case FIBMAP:

            {

                  struct address_space *mapping = filp->f_mapping;

                  int res;

                  /* do we support this mess? */

                  if (!mapping->a_ops->bmap)

                        return -EINVAL;

                  if (!capable(CAP_SYS_RAWIO))

                        return -EPERM;

                  if ((error = get_user(block, p)) != 0)

                        return error;

 

                  lock_kernel();

                  res = mapping->a_ops->bmap(mapping, block);

                  unlock_kernel();

                  return put_user(res, p);

            }

            case FIGETBSZ:

                  return put_user(inode->i_sb->s_blocksize, p);

            case FIONREAD:

                  return put_user(i_size_read(inode) - filp->f_pos, p);

      }

 

      return do_ioctl(filp, cmd, arg);

}

 

 

static long do_ioctl(struct file *filp, unsigned int cmd,

            unsigned long arg)

{

      int error = -ENOTTY;

      void *f;

 

      if (!filp->f_op)

            goto out;

 

      if (filp->f_op->unlocked_ioctl) {

            error = filp->f_op->unlocked_ioctl(filp, cmd, arg); // which is sock_ioctl for socket

            if (error == -ENOIOCTLCMD)

                  error = -EINVAL;

            goto out;

      } else if ((f = filp->f_op->ioctl)) {

            lock_kernel();

            if (!filp->f_op->ioctl) {

                  printk("%s: ioctl %p disappeared\n", __FUNCTION__, f);

                  print_symbol("symbol: %s\n", (unsigned long)f);

                  dump_stack();

            } else {

                  error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,

                                      filp, cmd, arg);

            }

            unlock_kernel();

      }

 

 out:

      return error;

}

 

static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)

{

      struct socket *sock;

      void __user *argp = (void __user *)arg;

      int pid, err;

 

      sock = file->private_data;

      if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {

            err = dev_ioctl(cmd, argp);

      } else

#ifdef CONFIG_WIRELESS_EXT

      if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {

            err = dev_ioctl(cmd, argp);

      } else

#endif                        /* CONFIG_WIRELESS_EXT */

            switch (cmd) {

            case FIOSETOWN:

            case SIOCSPGRP:

                  err = -EFAULT;

                  if (get_user(pid, (int __user *)argp))

                        break;

                  err = f_setown(sock->file, pid, 1);

                  break;

            case FIOGETOWN:

            case SIOCGPGRP:

                  err = put_user(f_getown(sock->file),

                               (int __user *)argp);

                  break;

            case SIOCGIFBR:

            case SIOCSIFBR:

            case SIOCBRADDBR:

            case SIOCBRDELBR:

                  err = -ENOPKG;

                  if (!br_ioctl_hook)

                        request_module("bridge");

 

                  mutex_lock(&br_ioctl_mutex);

                  if (br_ioctl_hook)

                        err = br_ioctl_hook(cmd, argp);

                  mutex_unlock(&br_ioctl_mutex);

                  break;

            case SIOCGIFVLAN:

            case SIOCSIFVLAN:

                  err = -ENOPKG;

                  if (!vlan_ioctl_hook)

                        request_module("8021q");

 

                  mutex_lock(&vlan_ioctl_mutex);

                  if (vlan_ioctl_hook)

                        err = vlan_ioctl_hook(argp);

                  mutex_unlock(&vlan_ioctl_mutex);

                  break;

            case SIOCADDDLCI:

            case SIOCDELDLCI:

                  err = -ENOPKG;

                  if (!dlci_ioctl_hook)

                        request_module("dlci");

 

                  if (dlci_ioctl_hook) {

                        mutex_lock(&dlci_ioctl_mutex);

                        err = dlci_ioctl_hook(cmd, argp);

                        mutex_unlock(&dlci_ioctl_mutex);

                  }

                  break;

            default:

                  err = sock->ops->ioctl(sock, cmd, arg); // inet_ioctl for AF_INET

 

                  /*

                   * If this ioctl is unknown try to hand it down

                   * to the NIC driver.

                   */

                  if (err == -ENOIOCTLCMD)

                        err = dev_ioctl(cmd, argp);

                  break;

            }

      return err;

}

 

int dev_ioctl(unsigned int cmd, void __user *arg)

{

      struct ifreq ifr;

      int ret;

      char *colon;

 

      /* One special case: SIOCGIFCONF takes ifconf argument

         and requires shared lock, because it sleeps writing

         to user space.

       */

 

      if (cmd == SIOCGIFCONF) {

            rtnl_lock();

            ret = dev_ifconf((char __user *) arg);

            rtnl_unlock();

            return ret;

      }

      if (cmd == SIOCGIFNAME)

            return dev_ifname((struct ifreq __user *)arg);

 

      if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))

            return -EFAULT;

 

      ifr.ifr_name[IFNAMSIZ-1] = 0;

 

      colon = strchr(ifr.ifr_name, ':');

      if (colon)

            *colon = 0;

 

      /*

       *    See which interface the caller is talking about.

       */

 

      switch (cmd) {

            /*

             *    These ioctl calls:

             *    - can be done by all.

             *    - atomic and do not require locking.

             *    - return a value

             */

            case SIOCGIFFLAGS:

            case SIOCGIFMETRIC:

            case SIOCGIFMTU:

            case SIOCGIFHWADDR:

            case SIOCGIFSLAVE:

            case SIOCGIFMAP:

            case SIOCGIFINDEX:

            case SIOCGIFTXQLEN:

                  dev_load(ifr.ifr_name);

                  read_lock(&dev_base_lock);

                  ret = dev_ifsioc(&ifr, cmd);

                  read_unlock(&dev_base_lock);

                  if (!ret) {

                        if (colon)

                              *colon = ':';

                        if (copy_to_user(arg, &ifr,

                                     sizeof(struct ifreq)))

                              ret = -EFAULT;

                  }

                  return ret;

 

            case SIOCETHTOOL:

                  dev_load(ifr.ifr_name);

                  rtnl_lock();

                  ret = dev_ethtool(&ifr);

                  rtnl_unlock();

                  if (!ret) {

                        if (colon)

                              *colon = ':';

                        if (copy_to_user(arg, &ifr,

                                     sizeof(struct ifreq)))

                              ret = -EFAULT;

                  }

                  return ret;

 

            /*

             *    These ioctl calls:

             *    - require superuser power.

             *    - require strict serialization.

             *    - return a value

             */

            case SIOCGMIIPHY:

            case SIOCGMIIREG:

            case SIOCSIFNAME:

                  if (!capable(CAP_NET_ADMIN))

                        return -EPERM;

                  dev_load(ifr.ifr_name);

                  rtnl_lock();

                  ret = dev_ifsioc(&ifr, cmd);

                  rtnl_unlock();

                  if (!ret) {

                        if (colon)

                              *colon = ':';

                        if (copy_to_user(arg, &ifr,

                                     sizeof(struct ifreq)))

                              ret = -EFAULT;

                  }

                  return ret;

 

            /*

             *    These ioctl calls:

             *    - require superuser power.

             *    - require strict serialization.

             *    - do not return a value

             */

            case SIOCSIFFLAGS:

            case SIOCSIFMETRIC:

            case SIOCSIFMTU:

            case SIOCSIFMAP:

            case SIOCSIFHWADDR:

            case SIOCSIFSLAVE:

            case SIOCADDMULTI:

            case SIOCDELMULTI:

            case SIOCSIFHWBROADCAST:

            case SIOCSIFTXQLEN:

            case SIOCSMIIREG:

            case SIOCBONDENSLAVE:

            case SIOCBONDRELEASE:

            case SIOCBONDSETHWADDR:

            case SIOCBONDCHANGEACTIVE:

            case SIOCBRADDIF:

            case SIOCBRDELIF:

                  if (!capable(CAP_NET_ADMIN))

                        return -EPERM;

                  /* fall through */

            case SIOCBONDSLAVEINFOQUERY:

            case SIOCBONDINFOQUERY:

                  dev_load(ifr.ifr_name);

                  rtnl_lock();

                  ret = dev_ifsioc(&ifr, cmd);

                  rtnl_unlock();

                  return ret;

 

            case SIOCGIFMEM:

                  /* Get the per device memory space. We can add this but

                   * currently do not support it */

            case SIOCSIFMEM:

                  /* Set the per device memory buffer space.

                   * Not applicable in our case */

            case SIOCSIFLINK:

                  return -EINVAL;

 

            /*

             *    Unknown or private ioctl.

             */

            default:

                  if (cmd == SIOCWANDEV ||

                      (cmd >= SIOCDEVPRIVATE &&

                       cmd <= SIOCDEVPRIVATE + 15)) {

                        dev_load(ifr.ifr_name);

                        rtnl_lock();

                        ret = dev_ifsioc(&ifr, cmd);

                        rtnl_unlock();

                        if (!ret && copy_to_user(arg, &ifr,

                                           sizeof(struct ifreq)))

                              ret = -EFAULT;

                        return ret;

                  }

                  /* Take care of Wireless Extensions */

                  if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)

                        return wext_handle_ioctl(&ifr, cmd, arg);

                  return -EINVAL;

      }

}

 

 

int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,

                  void __user *arg)

{

      int ret;

 

      /* If command is `set a parameter', or

       * `get the encoding parameters', check if

       * the user has the right to do it */

      if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)

          && !capable(CAP_NET_ADMIN))

            return -EPERM;

 

      dev_load(ifr->ifr_name);

      rtnl_lock();

      ret = wireless_process_ioctl(ifr, cmd);

      rtnl_unlock();

      if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq)))

            return -EFAULT;

      return ret;

}

 

/* ---------------------------------------------------------------- */

/*

 * Main IOCTl dispatcher.

 * Check the type of IOCTL and call the appropriate wrapper...

 */

static int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)

{

      struct net_device *dev;

      iw_handler  handler;

 

      /* Permissions are already checked in dev_ioctl() before calling us.

       * The copy_to/from_user() of ifr is also dealt with in there */

 

      /* Make sure the device exist */

      if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)

            return -ENODEV;

 

      /* A bunch of special cases, then the generic case...

       * Note that 'cmd' is already filtered in dev_ioctl() with

       * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */

      if (cmd == SIOCGIWSTATS)

            return ioctl_standard_call(dev, ifr, cmd,

                                 &iw_handler_get_iwstats);

 

      if (cmd == SIOCGIWPRIV && dev->wireless_handlers)

            return ioctl_standard_call(dev, ifr, cmd,

                                 &iw_handler_get_private);

 

      /* Basic check */

      if (!netif_device_present(dev))

            return -ENODEV;

 

      /* New driver API : try to find the handler */

      handler = get_handler(dev, cmd);

      if (handler) {

            /* Standard and private are not the same */

            if (cmd < SIOCIWFIRSTPRIV)

                  return ioctl_standard_call(dev, ifr, cmd, handler);

            else

                  return ioctl_private_call(dev, ifr, cmd, handler);

      }

      /* Old driver API : call driver ioctl handler */

      if (dev->do_ioctl)

            return dev->do_ioctl(dev, ifr, cmd);

      return -EOPNOTSUPP;

}

 

static iw_handler get_handler(struct net_device *dev, unsigned int cmd)

{

      /* Don't "optimise" the following variable, it will crash */

      unsigned int      index;            /* *MUST* be unsigned */

 

      /* Check if we have some wireless handlers defined */

      if (dev->wireless_handlers == NULL)

            return NULL;

 

      /* Try as a standard command */

      index = cmd - SIOCIWFIRST;

      if (index < dev->wireless_handlers->num_standard)

            return dev->wireless_handlers->standard[index];

 

      /* Try as a private command */

      index = cmd - SIOCIWFIRSTPRIV;

      if (index < dev->wireless_handlers->num_private)

            return dev->wireless_handlers->private[index];

 

      /* Not found */

      return NULL;

}

 

/************************** IOCTL SUPPORT **************************/

/*

 * The original user space API to configure all those Wireless Extensions

 * is through IOCTLs.

 * In there, we check if we need to call the new driver API (iw_handler)

 * or just call the driver ioctl handler.

 */

 

/* ---------------------------------------------------------------- */

/*

 * Wrapper to call a standard Wireless Extension handler.

 * We do various checks and also take care of moving data between

 * user space and kernel space.

 */

static int ioctl_standard_call(struct net_device *    dev,

                         struct ifreq *         ifr,

                         unsigned int           cmd,

                         iw_handler       handler)

{

      struct iwreq *                      iwr = (struct iwreq *) ifr;

      const struct iw_ioctl_description * descr;

      struct iw_request_info              info;

      int                           ret = -EINVAL;

 

      /* Get the description of the IOCTL */

      if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)

            return -EOPNOTSUPP;

      descr = &(standard_ioctl[cmd - SIOCIWFIRST]);

 

      /* Prepare the call */

      info.cmd = cmd;

      info.flags = 0;

 

      /* Check if we have a pointer to user space data or not */

      if (descr->header_type != IW_HEADER_TYPE_POINT) {

 

            /* No extra arguments. Trivial to handle */

            ret = handler(dev, &info, &(iwr->u), NULL);

 

            /* Generate an event to notify listeners of the change */

            if ((descr->flags & IW_DESCR_FLAG_EVENT) &&

               ((ret == 0) || (ret == -EIWCOMMIT)))

                  wireless_send_event(dev, cmd, &(iwr->u), NULL);

      } else {

            char *      extra;

            int   extra_size;

            int   user_length = 0;

            int   err;

            int   essid_compat = 0;

 

            /* Calculate space needed by arguments. Always allocate

             * for max space. Easier, and won't last long... */

            extra_size = descr->max_tokens * descr->token_size;

 

            /* Check need for ESSID compatibility for WE < 21 */

            switch (cmd) {

            case SIOCSIWESSID:

            case SIOCGIWESSID:

            case SIOCSIWNICKN:

            case SIOCGIWNICKN:

                  if (iwr->u.data.length == descr->max_tokens + 1)

                        essid_compat = 1;

                  else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {

                        char essid[IW_ESSID_MAX_SIZE + 1];

 

                        err = copy_from_user(essid, iwr->u.data.pointer,

                                         iwr->u.data.length *

                                         descr->token_size);

                        if (err)

                              return -EFAULT;

 

                        if (essid[iwr->u.data.length - 1] == '\0')

                              essid_compat = 1;

                  }

                  break;

            default:

                  break;

            }

 

            iwr->u.data.length -= essid_compat;

 

            /* Check what user space is giving us */

            if (IW_IS_SET(cmd)) {

                  /* Check NULL pointer */

                  if ((iwr->u.data.pointer == NULL) &&

                     (iwr->u.data.length != 0))

                        return -EFAULT;

                  /* Check if number of token fits within bounds */

                  if (iwr->u.data.length > descr->max_tokens)

                        return -E2BIG;

                  if (iwr->u.data.length < descr->min_tokens)

                        return -EINVAL;

            } else {

                  /* Check NULL pointer */

                  if (iwr->u.data.pointer == NULL)

                        return -EFAULT;

                  /* Save user space buffer size for checking */

                  user_length = iwr->u.data.length;

 

                  /* Don't check if user_length > max to allow forward

                   * compatibility. The test user_length < min is

                   * implied by the test at the end. */

 

                  /* Support for very large requests */

                  if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&

                     (user_length > descr->max_tokens)) {

                        /* Allow userspace to GET more than max so

                         * we can support any size GET requests.

                         * There is still a limit : -ENOMEM. */

                        extra_size = user_length * descr->token_size;

                       /* Note : user_length is originally a __u16,

                         * and token_size is controlled by us,

                         * so extra_size won't get negative and

                         * won't overflow... */

                  }

            }

 

            /* Create the kernel buffer */

            /*    kzalloc ensures NULL-termination for essid_compat */

            extra = kzalloc(extra_size, GFP_KERNEL);

            if (extra == NULL)

                  return -ENOMEM;

 

            /* If it is a SET, get all the extra data in here */

            if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {

                  err = copy_from_user(extra, iwr->u.data.pointer,

                                   iwr->u.data.length *

                                   descr->token_size);

                  if (err) {

                        kfree(extra);

                        return -EFAULT;

                  }

            }

 

            /* Call the handler */

            ret = handler(dev, &info, &(iwr->u), extra);

 

            iwr->u.data.length += essid_compat;

 

            /* If we have something to return to the user */

            if (!ret && IW_IS_GET(cmd)) {

                  /* Check if there is enough buffer up there */

                  if (user_length < iwr->u.data.length) {

                        kfree(extra);

                        return -E2BIG;

                  }

 

                  err = copy_to_user(iwr->u.data.pointer, extra,

                                 iwr->u.data.length *

                                 descr->token_size);

                  if (err)

                        ret =  -EFAULT;

            }

 

            /* Generate an event to notify listeners of the change */

            if ((descr->flags & IW_DESCR_FLAG_EVENT) &&

               ((ret == 0) || (ret == -EIWCOMMIT))) {

                  if (descr->flags & IW_DESCR_FLAG_RESTRICT)

                        /* If the event is restricted, don't

                         * export the payload */

                        wireless_send_event(dev, cmd, &(iwr->u), NULL);

                  else

                        wireless_send_event(dev, cmd, &(iwr->u),

                                        extra);

            }

 

            /* Cleanup - I told you it wasn't that long ;-) */

            kfree(extra);

      }

 

      /* Call commit handler if needed and defined */

      if (ret == -EIWCOMMIT)

            ret = call_commit_handler(dev);

 

      /* Here, we will generate the appropriate event if needed */

 

      return ret;

}

 

/* ---------------------------------------------------------------- */

/*

 * Wrapper to call a private Wireless Extension handler.

 * We do various checks and also take care of moving data between

 * user space and kernel space.

 * It's not as nice and slimline as the standard wrapper. The cause

 * is struct iw_priv_args, which was not really designed for the

 * job we are going here.

 *

 * IMPORTANT : This function prevent to set and get data on the same

 * IOCTL and enforce the SET/GET convention. Not doing it would be

 * far too hairy...

 * If you need to set and get data at the same time, please don't use

 * a iw_handler but process it in your ioctl handler (i.e. use the

 * old driver API).

 */

static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr,

                        unsigned int cmd, iw_handler handler)

{

      struct iwreq *                iwr = (struct iwreq *) ifr;

      const struct iw_priv_args *   descr = NULL;

      struct iw_request_info        info;

      int                     extra_size = 0;

      int                     i;

      int                     ret = -EINVAL;

 

      /* Get the description of the IOCTL */

      for (i = 0; i < dev->wireless_handlers->num_private_args; i++)

            if (cmd == dev->wireless_handlers->private_args[i].cmd) {

                  descr = &(dev->wireless_handlers->private_args[i]);

                  break;

            }

 

      /* Compute the size of the set/get arguments */

      if (descr != NULL) {

            if (IW_IS_SET(cmd)) {

                  int   offset = 0; /* For sub-ioctls */

                  /* Check for sub-ioctl handler */

                  if (descr->name[0] == '\0')

                        /* Reserve one int for sub-ioctl index */

                        offset = sizeof(__u32);

 

                  /* Size of set arguments */

                  extra_size = get_priv_size(descr->set_args);

 

                  /* Does it fits in iwr ? */

                  if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&

                     ((extra_size + offset) <= IFNAMSIZ))

                        extra_size = 0;

            } else {

                  /* Size of get arguments */

                  extra_size = get_priv_size(descr->get_args);

 

                  /* Does it fits in iwr ? */

                  if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&

                     (extra_size <= IFNAMSIZ))

                        extra_size = 0;

            }

      }

 

      /* Prepare the call */

      info.cmd = cmd;

      info.flags = 0;

 

      /* Check if we have a pointer to user space data or not. */

      if (extra_size == 0) {

            /* No extra arguments. Trivial to handle */

            ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));

      } else {

            char *      extra;

            int   err;

 

            /* Check what user space is giving us */

            if (IW_IS_SET(cmd)) {

                  /* Check NULL pointer */

                  if ((iwr->u.data.pointer == NULL) &&

                     (iwr->u.data.length != 0))

                        return -EFAULT;

 

                  /* Does it fits within bounds ? */

                  if (iwr->u.data.length > (descr->set_args &

                                     IW_PRIV_SIZE_MASK))

                        return -E2BIG;

            } else if (iwr->u.data.pointer == NULL)

                  return -EFAULT;

 

            /* Always allocate for max space. Easier, and won't last

             * long... */

            extra = kmalloc(extra_size, GFP_KERNEL);

            if (extra == NULL)

                  return -ENOMEM;

 

            /* If it is a SET, get all the extra data in here */

            if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {

                  err = copy_from_user(extra, iwr->u.data.pointer,

                                   extra_size);

                  if (err) {

                        kfree(extra);

                        return -EFAULT;

                  }

            }

 

            /* Call the handler */

            ret = handler(dev, &info, &(iwr->u), extra);

 

            /* If we have something to return to the user */

            if (!ret && IW_IS_GET(cmd)) {

 

                  /* Adjust for the actual length if it's variable,

                   * avoid leaking kernel bits outside. */

                  if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {

                        extra_size = adjust_priv_size(descr->get_args,

                                                &(iwr->u));

                  }

 

                  err = copy_to_user(iwr->u.data.pointer, extra,

                                 extra_size);

                  if (err)

                        ret =  -EFAULT;

            }

 

            /* Cleanup - I told you it wasn't that long ;-) */

            kfree(extra);

      }

 

 

      /* Call commit handler if needed and defined */

      if (ret == -EIWCOMMIT)

            ret = call_commit_handler(dev);

 

      return ret;

}

 

import socket import struct import threading import re import time import tkinter as tk from tkinter import ttk, scrolledtext, messagebox import sys import os import platform class SimpleIDS: def __init__(self): # 检测规则 self.rules = [ { "name": "SQL Injection", "pattern": re.compile(r'(union\s+select|select\s+.+\s+from|insert\s+into|1=1|drop\s+table)', re.IGNORECASE), "description": "SQL注入攻击尝试" }, { "name": "XSS Attack", "pattern": re.compile(r'(<script>|alert\(|onerror=|javascript:|document\.cookie)', re.IGNORECASE), "description": "跨站脚本攻击尝试" }, { "name": "Directory Traversal", "pattern": re.compile(r'(\.\./|\.\.\\|etc/passwd|win\.ini)', re.IGNORECASE), "description": "路径遍历攻击尝试" }, { "name": "Command Injection", "pattern": re.compile(r'(;\s*(rm|sh|bash|cmd|powershell)|\|\s*(rm|sh|bash|cmd))', re.IGNORECASE), "description": "命令注入攻击尝试" }, { "name": "Port Scan Detection", "pattern": None, "description": "检测到端口扫描行为" } ] # IDS状态 self.running = False self.socket = None self.thread = None self.packet_count = 0 self.alert_count = 0 self.start_time = time.time() # 连接跟踪(用于检测端口扫描) self.connection_tracker = {} self.scan_threshold = 10 # 10秒内10个连接视为扫描 # 创建界面 self.create_gui() def get_network_interfaces(self): """获取可用的网络接口""" interfaces = [] try: if platform.system() == 'Windows': # 简化版Windows接口获取 interfaces = ["所有接口"] # Windows上原始套接字需要管理员权限,通常无法直接指定接口 else: # Linux/MacOS系统获取网络接口 interfaces = ["所有接口"] + [iface[1] for iface in socket.if_nameindex()] except: interfaces = ["所有接口"] return interfaces def create_gui(self): """创建图形用户界面""" self.root = tk.Tk() self.root.title("纯Python入侵检测系统") self.root.geometry("800x600") self.root.protocol("WM_DELETE_WINDOW", self.on_close) # 创建主框架 main_frame = ttk.Frame(self.root, padding="10") main_frame.pack(fill=tk.BOTH, expand=True) # 控制面板 control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding="10") control_frame.pack(fill=tk.X, pady=5) # 接口选择 interface_frame = ttk.Frame(control_frame) interface_frame.pack(fill=tk.X, pady=5) ttk.Label(interface_frame, text="网络接口:").pack(side=tk.LEFT, padx=5) self.interface_var = tk.StringVar() # 获取可用接口 interfaces = self.get_network_interfaces() self.interface_combo = ttk.Combobox(interface_frame, textvariable=self.interface_var, values=interfaces) self.interface_combo.current(0) # 默认选择第一个 self.interface_combo.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) # 按钮区域 button_frame = ttk.Frame(control_frame) button_frame.pack(fill=tk.X, pady=10) self.start_button = ttk.Button(button_frame, text="启动监控", command=self.start_monitoring) self.start_button.pack(side=tk.LEFT, padx=5) self.stop_button = ttk.Button(button_frame, text="停止监控", command=self.stop_monitoring, state=tk.DISABLED) self.stop_button.pack(side=tk.LEFT, padx=5) self.clear_button = ttk.Button(button_frame, text="清除日志", command=self.clear_logs) self.clear_button.pack(side=tk.LEFT, padx=5) # 统计信息 stats_frame = ttk.Frame(control_frame) stats_frame.pack(fill=tk.X, pady=10) stats_columns = ["packets", "alerts", "runtime"] for col in stats_columns: frame = ttk.Frame(stats_frame) frame.pack(side=tk.LEFT, padx=10, fill=tk.X, expand=True) ttk.Label(frame, text=col.replace("_", " ").title() + ":").pack(anchor=tk.W) var = tk.StringVar() var.set("0") setattr(self, f"{col}_var", var) ttk.Label(frame, textvariable=var, font=("Arial", 12, "bold")).pack(anchor=tk.W) # 日志区域 log_frame = ttk.LabelFrame(main_frame, text="检测日志", padding="10") log_frame.pack(fill=tk.BOTH, expand=True, pady=5) self.log_text = scrolledtext.ScrolledText( log_frame, wrap=tk.WORD, state=tk.DISABLED ) self.log_text.pack(fill=tk.BOTH, expand=True) # 规则面板 rule_frame = ttk.LabelFrame(main_frame, text="检测规则", padding="10") rule_frame.pack(fill=tk.X, pady=5) self.rule_tree = ttk.Treeview(rule_frame, columns=("description"), show="headings") self.rule_tree.heading("#0", text="规则名称") self.rule_tree.heading("description", text="描述") self.rule_tree.column("#0", width=150) self.rule_tree.column("description", width=500) scrollbar = ttk.Scrollbar(rule_frame, orient=tk.VERTICAL, command=self.rule_tree.yview) self.rule_tree.configure(yscrollcommand=scrollbar.set) self.rule_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 填充规则 for rule in self.rules: self.rule_tree.insert("", tk.END, text=rule["name"], values=(rule["description"],)) # 状态栏 self.status_var = tk.StringVar() self.status_var.set("就绪 - 请点击'启动监控'开始") status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) def log_message(self, message): """在日志区域添加消息""" self.log_text.config(state=tk.NORMAL) timestamp = time.strftime("%Y-%m-%d %H:%M:%S") self.log_text.insert(tk.END, f"[{timestamp}] {message}\n") self.log_text.config(state=tk.DISABLED) self.log_text.yview(tk.END) # 自动滚动到底部 def update_stats(self): """更新统计信息""" runtime = int(time.time() - self.start_time) hours, remainder = divmod(runtime, 3600) minutes, seconds = divmod(remainder, 60) runtime_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}" self.packets_var.set(str(self.packet_count)) self.alerts_var.set(str(self.alert_count)) self.runtime_var.set(runtime_str) if self.running: self.root.after(1000, self.update_stats) # 每秒更新一次 def start_monitoring(self): """开始监控网络流量""" if self.running: return try: # 创建原始套接字 if platform.system() == 'Windows': # Windows系统使用不同的套接字类型 self.socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP) self.socket.bind(("0.0.0.0", 0)) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) self.socket.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) else: # Linux/MacOS系统 self.socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003)) self.socket.settimeout(1) self.running = True self.start_time = time.time() self.packet_count = 0 self.alert_count = 0 # 启动监控线程 self.thread = threading.Thread(target=self.capture_packets) self.thread.daemon = True self.thread.start() # 更新UI self.start_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) self.status_var.set("监控中...") self.log_message("启动网络流量监控") # 开始更新统计信息 self.update_stats() except PermissionError: self.log_message("启动监控失败: 需要管理员/root权限") messagebox.showerror("权限错误", "请以管理员/root权限运行此程序") self.running = False except Exception as e: self.log_message(f"启动监控失败: {str(e)}") self.running = False def stop_monitoring(self): """停止监控网络流量""" if not self.running: return self.running = False if platform.system() == 'Windows' and self.socket: try: self.socket.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) except: pass if self.socket: try: self.socket.close() except: pass if self.thread and self.thread.is_alive(): self.thread.join(timeout=2) self.start_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) self.status_var.set("监控已停止") self.log_message("网络流量监控已停止") def clear_logs(self): """清除日志""" self.log_text.config(state=tk.NORMAL) self.log_text.delete(1.0, tk.END) self.log_text.config(state=tk.DISABLED) self.log_message("日志已清除") def on_close(self): """处理窗口关闭事件""" self.stop_monitoring() self.root.destroy() def capture_packets(self): """捕获网络数据包""" while self.running: try: packet = self.socket.recvfrom(65535)[0] self.packet_count += 1 self.parse_packet(packet) except socket.timeout: continue except Exception as e: if self.running: self.log_message(f"捕获数据包时出错: {str(e)}") continue def parse_packet(self, packet): """解析网络数据包""" try: if platform.system() == 'Windows': # Windows系统解析 ip_header = packet[:20] iph = struct.unpack('!BBHHHBBH4s4s', ip_header) version_ihl = iph[0] ihl = version_ihl & 0xF iph_length = ihl * 4 protocol = iph[6] if protocol != 6: # 只处理TCP return tcp_header = packet[iph_length:iph_length + 20] tcph = struct.unpack('!HHLLBBHHH', tcp_header) data_offset = (tcph[4] >> 4) * 4 tcp_data = packet[iph_length + data_offset:] try: payload = tcp_data.decode('utf-8', errors='ignore') except: payload = str(tcp_data)[2:-1] s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) source_port = tcph[0] dest_port = tcph[1] self.detect_attack(s_addr, d_addr, source_port, dest_port, payload) else: # Linux/MacOS系统解析 eth_length = 14 eth_header = packet[:eth_length] eth = struct.unpack('!6s6sH', eth_header) eth_protocol = socket.ntohs(eth[2]) if eth_protocol != 8: # 只处理IP return ip_header = packet[eth_length:eth_length + 20] iph = struct.unpack('!BBHHHBBH4s4s', ip_header) version_ihl = iph[0] ihl = version_ihl & 0xF iph_length = ihl * 4 protocol = iph[6] if protocol != 6: # 只处理TCP return tcp_header = packet[eth_length + iph_length:eth_length + iph_length + 20] tcph = struct.unpack('!HHLLBBHHH', tcp_header) data_offset = (tcph[4] >> 4) * 4 tcp_data = packet[eth_length + iph_length + data_offset:] try: payload = tcp_data.decode('utf-8', errors='ignore') except: payload = str(tcp_data)[2:-1] s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) source_port = tcph[0] dest_port = tcph[1] self.detect_attack(s_addr, d_addr, source_port, dest_port, payload) except Exception as e: pass def detect_attack(self, src_ip, dst_ip, src_port, dst_port, payload): """检测攻击模式""" self.detect_port_scan(src_ip, dst_ip, dst_port) for rule in self.rules: if rule["name"] == "Port Scan Detection": continue if rule["pattern"] and rule["pattern"].search(payload): self.alert_count += 1 alert_msg = ( f"检测到攻击: {rule['name']}\n" f"源IP: {src_ip}:{src_port} -> 目标: {dst_ip}:{dst_port}\n" f"描述: {rule['description']}\n" f"负载样本: {payload[:100]}" ) self.log_message(alert_msg) return def detect_port_scan(self, src_ip, dst_ip, dst_port): """检测端口扫描行为""" now = time.time() key = f"{src_ip}-{dst_ip}" if key not in self.connection_tracker: self.connection_tracker[key] = { "ports": set(), "count": 0, "last_seen": now, "alerted": False } tracker = self.connection_tracker[key] tracker["count"] += 1 tracker["ports"].add(dst_port) tracker["last_seen"] = now if len(tracker["ports"]) > 5 and tracker["count"] > self.scan_threshold: if not tracker["alerted"]: tracker["alerted"] = True self.alert_count += 1 alert_msg = ( f"检测到攻击: Port Scan Detection\n" f"源IP: {src_ip} -> 目标: {dst_ip}\n" f"描述: 检测到端口扫描行为\n" f"扫描端口数: {len(tracker['ports'])}" ) self.log_message(alert_msg) def run(self): """运行应用程序""" self.root.mainloop() def check_admin(): """检查管理员权限""" if platform.system() == 'Windows': try: import ctypes return ctypes.windll.shell32.IsUserAnAdmin() except: return False else: return os.geteuid() == 0 if __name__ == "__main__": if not check_admin(): print("警告: 需要管理员/root权限运行此脚本以捕获网络流量") print("在Windows上请右键选择'以管理员身份运行'") print("在Linux/MacOS上请使用sudo运行") # 仍然允许运行,但可能无法捕获流量 app = SimpleIDS() 修改这段代码的错误并且让此参数可以运行
07-06
import socket import struct import threading import re import time import tkinter as tk from tkinter import ttk, scrolledtext, messagebox import sys import os import platform class SimpleIDS: def __init__(self): # 增强版检测规则 - 优化正则表达式减少误报 self.rules = [ { "name": "SQL Injection", "pattern": re.compile( r'(?:(?:union(?:\s+all)?\s+select)|(?:select\s+.+?\s+from\s+\w)|(?:insert\s+into.+?values)|' r'(?:drop\s+(?:table|database)\s+\w)|(?:delete\s+from\s+\w)|(?:update\s+\w+\s+set)|' r'(?:\w+\s*=\s*\w*;)|(?:\d+\s+or\s+\d+))', re.IGNORECASE), "description": "SQL注入攻击尝试" }, { "name": "XSS Attack", "pattern": re.compile( r'(?:<script[^>]*>.*?<\/script>)|(?:on\w+\s*=\s*["\'].*?["\'])|' r'(?:javascript:)|(?:document\.(?:cookie|location))|(?:alert\(|eval\(|window\[["\'])', re.IGNORECASE), "description": "跨站脚本攻击尝试" }, { "name": "Directory Traversal", "pattern": re.compile( r'(?:\.\./|\.\.\\|%2e%2e%2f|%2e%2e\\|/etc/passwd|/win\.ini|' r'C:\\windows\\system32\\drivers\\etc\\hosts)', re.IGNORECASE), "description": "路径遍历攻击尝试" }, { "name": "Command Injection", "pattern": re.compile( r'(?:;\s*(?:rm|sh|bash|cmd|powershell|nc|wget|curl)\s)|' r'(?:\|\|?\s*(?:rm|sh|bash|cmd|powershell))|(?:&&?\s*(?:rm|sh|bash|cmd|powershell))|' r'(?:`(?:rm|sh|bash|cmd|powershell)`)', re.IGNORECASE), "description": "命令注入攻击尝试" }, { "name": "Port Scan Detection", "pattern": None, "description": "检测到端口扫描行为" } ] # IDS状态参数 self.running = False self.socket = None self.thread = None self.packet_count = 0 self.alert_count = 0 self.start_time = time.time() # 连接跟踪参数 self.connection_tracker = {} self.scan_threshold = 5 # 5秒内5个连接视为扫描(更敏感) self.scan_port_count_threshold = 3 # 扫描端口数量阈值 self.tracking_timeout = 30 # 跟踪记录超时时间(秒) # 创建界面 self.create_gui() def get_network_interfaces(self): """获取可用的网络接口""" interfaces = [] try: if platform.system() == 'Windows': # Windows上简化处理 interfaces = ["所有接口"] else: try: interfaces = ["所有接口"] + [iface[1] for iface in socket.if_nameindex()] except: interfaces = ["所有接口"] except Exception as e: print(f"获取网络接口失败: {e}") interfaces = ["所有接口"] return interfaces def create_gui(self): """创建图形用户界面""" self.root = tk.Tk() self.root.title("增强版Python入侵检测系统") self.root.geometry("900x700") self.root.protocol("WM_DELETE_WINDOW", self.on_close) # 主框架 main_frame = ttk.Frame(self.root, padding="10") main_frame.pack(fill=tk.BOTH, expand=True) # 控制面板 control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding="10") control_frame.pack(fill=tk.X, pady=5) # 接口选择 interface_frame = ttk.Frame(control_frame) interface_frame.pack(fill=tk.X, pady=5) ttk.Label(interface_frame, text="网络接口:").pack(side=tk.LEFT, padx=5) self.interface_var = tk.StringVar() interfaces = self.get_network_interfaces() self.interface_combo = ttk.Combobox(interface_frame, textvariable=self.interface_var, values=interfaces) self.interface_combo.current(0) self.interface_combo.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) # 参数配置区域 param_frame = ttk.LabelFrame(control_frame, text="检测参数", padding="10") param_frame.pack(fill=tk.X, pady=5) # 扫描检测阈值配置 ttk.Label(param_frame, text="扫描端口数阈值:").grid(row=0, column=0, padx=5, pady=2, sticky=tk.W) self.port_count_threshold = tk.IntVar(value=self.scan_port_count_threshold) ttk.Entry(param_frame, textvariable=self.port_count_threshold, width=5).grid(row=0, column=1, padx=5, pady=2) ttk.Label(param_frame, text="扫描时间窗口(秒):").grid(row=0, column=2, padx=5, pady=2, sticky=tk.W) self.scan_time_window = tk.IntVar(value=self.scan_threshold) ttk.Entry(param_frame, textvariable=self.scan_time_window, width=5).grid(row=0, column=3, padx=5, pady=2) # 按钮区域 button_frame = ttk.Frame(control_frame) button_frame.pack(fill=tk.X, pady=10) self.start_button = ttk.Button(button_frame, text="启动监控", command=self.start_monitoring) self.start_button.pack(side=tk.LEFT, padx=5) self.stop_button = ttk.Button(button_frame, text="停止监控", command=self.stop_monitoring, state=tk.DISABLED) self.stop_button.pack(side=tk.LEFT, padx=5) self.clear_button = ttk.Button(button_frame, text="清除日志", command=self.clear_logs) self.clear_button.pack(side=tk.LEFT, padx=5) # 统计信息 stats_frame = ttk.Frame(control_frame) stats_frame.pack(fill=tk.X, pady=10) stats_columns = ["packets", "alerts", "runtime"] for col in stats_columns: frame = ttk.Frame(stats_frame) frame.pack(side=tk.LEFT, padx=10, fill=tk.X, expand=True) ttk.Label(frame, text=col.replace("_", " ").title() + ":").pack(anchor=tk.W) var = tk.StringVar() var.set("0") setattr(self, f"{col}_var", var) ttk.Label(frame, textvariable=var, font=("Arial", 12, "bold")).pack(anchor=tk.W) # 日志区域 log_frame = ttk.LabelFrame(main_frame, text="检测日志", padding="10") log_frame.pack(fill=tk.BOTH, expand=True, pady=5) self.log_text = scrolledtext.ScrolledText( log_frame, wrap=tk.WORD, state=tk.DISABLED, font=("Consolas", 10) ) self.log_text.pack(fill=tk.BOTH, expand=True) # 规则面板 rule_frame = ttk.LabelFrame(main_frame, text="检测规则", padding="10") rule_frame.pack(fill=tk.X, pady=5) self.rule_tree = ttk.Treeview(rule_frame, columns=("description",), show="headings") self.rule_tree.heading("#0", text="规则名称") self.rule_tree.heading("description", text="描述") self.rule_tree.column("#0", width=150) self.rule_tree.column("description", width=500) scrollbar = ttk.Scrollbar(rule_frame, orient=tk.VERTICAL, command=self.rule_tree.yview) self.rule_tree.configure(yscrollcommand=scrollbar.set) self.rule_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 填充规则 for rule in self.rules: self.rule_tree.insert("", tk.END, text=rule["name"], values=(rule["description"],)) # 状态栏 self.status_var = tk.StringVar() self.status_var.set("就绪 - 请点击'启动监控'开始") status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) def log_message(self, message, level="info"): """在日志区域添加消息""" self.log_text.config(state=tk.NORMAL) timestamp = time.strftime("%Y-%m-%d %H:%M:%S") level_tag = {"info": "[INFO]", "warning": "[WARN]", "alert": "[ALERT]"}.get(level, "[INFO]") self.log_text.insert(tk.END, f"{level_tag} [{timestamp}] {message}\n") self.log_text.config(state=tk.DISABLED) self.log_text.yview(tk.END) def update_stats(self): """更新统计信息""" runtime = int(time.time() - self.start_time) hours, remainder = divmod(runtime, 3600) minutes, seconds = divmod(remainder, 60) runtime_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}" self.packets_var.set(str(self.packet_count)) self.alerts_var.set(str(self.alert_count)) self.runtime_var.set(runtime_str) if self.running: self.root.after(1000, self.update_stats) def start_monitoring(self): """开始监控网络流量""" if self.running: return # 更新扫描检测参数 self.scan_threshold = self.scan_time_window.get() self.scan_port_count_threshold = self.port_count_threshold.get() try: # 创建原始套接字 if platform.system() == 'Windows': self.socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP) self.socket.bind(("0.0.0.0", 0)) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) self.socket.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) else: self.socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003)) self.socket.settimeout(1) self.running = True self.start_time = time.time() self.packet_count = 0 self.alert_count = 0 # 启动监控线程 self.thread = threading.Thread(target=self.capture_packets) self.thread.daemon = True self.thread.start() # 更新UI self.start_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) self.status_var.set("监控中...") self.log_message("启动网络流量监控", "info") # 开始更新统计信息 self.update_stats() except PermissionError: self.log_message("启动监控失败: 需要管理员/root权限", "warning") messagebox.showerror("权限错误", "请以管理员/root权限运行此程序") self.running = False except Exception as e: self.log_message(f"启动监控失败: {str(e)}", "warning") self.running = False def stop_monitoring(self): """停止监控网络流量""" if not self.running: return self.running = False if platform.system() == 'Windows' and self.socket: try: self.socket.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) except Exception as e: self.log_message(f"关闭Windows监听模式失败: {e}", "warning") if self.socket: try: self.socket.close() except Exception as e: self.log_message(f"关闭套接字失败: {e}", "warning") if self.thread and self.thread.is_alive(): self.thread.join(timeout=2) self.start_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) self.status_var.set("监控已停止") self.log_message("网络流量监控已停止", "info") def clear_logs(self): """清除日志""" self.log_text.config(state=tk.NORMAL) self.log_text.delete(1.0, tk.END) self.log_text.config(state=tk.DISABLED) self.log_message("日志已清除", "info") def on_close(self): """处理窗口关闭事件""" self.stop_monitoring() self.root.destroy() def capture_packets(self): """捕获网络数据包""" while self.running: try: packet = self.socket.recvfrom(65535)[0] self.packet_count += 1 self.parse_packet(packet) except socket.timeout: continue except Exception as e: if self.running: self.log_message(f"捕获数据包时出错: {str(e)}", "warning") continue def parse_packet(self, packet): """解析网络数据包""" try: if platform.system() == 'Windows': # Windows系统解析 if len(packet) < 20: # 最小IP头长度 return ip_header = packet[:20] iph = struct.unpack('!BBHHHBBH4s4s', ip_header) version_ihl = iph[0] ihl = version_ihl & 0xF iph_length = ihl * 4 protocol = iph[6] if protocol != 6: # 只处理TCP return if len(packet) < iph_length + 20: # 检查是否有完整的TCP头 return tcp_header = packet[iph_length:iph_length+20] tcph = struct.unpack('!HHLLBBHHH', tcp_header) data_offset = (tcph[4] >> 4) * 4 tcp_data = packet[iph_length+data_offset:] try: payload = tcp_data.decode('utf-8', errors='ignore') except Exception as e: payload = str(tcp_data)[2:-1] s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) source_port = tcph[0] dest_port = tcph[1] self.detect_attack(s_addr, d_addr, source_port, dest_port, payload) else: # Linux/MacOS系统解析 if len(packet) < 14: # 最小以太网头长度 return eth_length = 14 eth_header = packet[:eth_length] eth = struct.unpack('!6s6sH', eth_header) eth_protocol = socket.ntohs(eth[2]) if eth_protocol != 8: # 只处理IP return if len(packet) < eth_length + 20: # 检查是否有完整的IP头 return ip_header = packet[eth_length:eth_length+20] iph = struct.unpack('!BBHHHBBH4s4s', ip_header) version_ihl = iph[0] ihl = version_ihl & 0xF iph_length = ihl * 4 protocol = iph[6] if protocol != 6: # 只处理TCP return if len(packet) < eth_length + iph_length + 20: # 检查是否有完整的TCP头 return tcp_header = packet[eth_length+iph_length:eth_length+iph_length+20] tcph = struct.unpack('!HHLLBBHHH', tcp_header) data_offset = (tcph[4] >> 4) * 4 tcp_data = packet[eth_length+iph_length+data_offset:] try: payload = tcp_data.decode('utf-8', errors='ignore') except Exception as e: payload = str(tcp_data)[2:-1] s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) source_port = tcph[0] dest_port = tcph[1] self.detect_attack(s_addr, d_addr, source_port, dest_port, payload) except Exception as e: if self.running: self.log_message(f"解析数据包时出错: {str(e)}", "warning") def detect_attack(self, src_ip, dst_ip, src_port, dst_port, payload): """检测攻击模式""" self.detect_port_scan(src_ip, dst_ip, dst_port) for rule in self.rules: if rule["name"] == "Port Scan Detection" or not rule["pattern"]: continue if rule["pattern"].search(payload): self.alert_count += 1 alert_msg = ( f"检测到攻击: {rule['name']}\n" f"源IP: {src_ip}:{src_port} -> 目标: {dst_ip}:{dst_port}\n" f"描述: {rule['description']}\n" f"负载样本: {payload[:100]}" ) self.log_message(alert_msg, "alert") return def detect_port_scan(self, src_ip, dst_ip, dst_port): """增强版端口扫描检测""" now = time.time() key = f"{src_ip}-{dst_ip}" # 清理过期记录 expired_keys = [k for k, v in self.connection_tracker.items() if now - v["last_seen"] > self.tracking_timeout] for k in expired_keys: del self.connection_tracker[k] # 初始化或更新跟踪记录 if key not in self.connection_tracker: self.connection_tracker[key] = { "ports": set(), "count": 0, "first_seen": now, "last_seen": now, "alerted": False } tracker = self.connection_tracker[key] tracker["count"] += 1 tracker["ports"].add(dst_port) tracker["last_seen"] = now # 检测扫描行为 time_window = now - tracker["first_seen"] if (len(tracker["ports"]) >= self.scan_port_count_threshold and tracker["count"] >= self.scan_threshold and time_window <= self.scan_threshold): if not tracker["alerted"]: tracker["alerted"] = True self.alert_count += 1 alert_msg = ( f"检测到攻击: Port Scan Detection\n" f"源IP: {src_ip} -> 目标: {dst_ip}\n" f"描述: 检测到端口扫描行为\n" f"扫描端口数: {len(tracker['ports'])}\n" f"连接次数: {tracker['count']}\n" f"时间窗口: {time_window:.2f}秒" ) self.log_message(alert_msg, "alert") def run(self): """运行应用程序""" try: self.root.mainloop() except Exception as e: self.log_message(f"GUI运行错误: {str(e)}", "warning") self.on_close() def check_admin(): """检查管理员权限""" if platform.system() == 'Windows': try: import ctypes return ctypes.windll.shell32.IsUserAnAdmin() except: return False else: return os.geteuid() == 0 if __name__ == "__main__": if not check_admin(): print("警告: 需要管理员/root权限运行此脚本以捕获网络流量") print("在Windows上请右键选择'以管理员身份运行'") print("在Linux/MacOS上请使用sudo运行") try: app = SimpleIDS() app.run() except Exception as e: print(f"程序启动失败: {e}") sys.exit(1) 我用Pycharm运行此代码显示[WARN] [2025-07-05 15:28:56] 启动监控失败: [WinError 10022] 提供了一个无效的参数,请改正其错误,并且使其运行具有有效参数
最新发布
07-06
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值