/************************************* */
/* Rule Set Based Access Control       */
/* Author and (c) 1999-2002:           */
/*   Amon Ott <ao@rsbac.org>           */
/* Helper functions for network        */
/* Last modified: 24/Apr/2002          */
/************************************* */

#include <rsbac/types.h>
#ifdef __KERNEL__
#include <rsbac/network.h>
#endif

__u32 rsbac_net_make_mask_u32(__u8 bits)
  {
    __u32 res;
    __u8 res0 = 0;
    __u8 res1 = 0;
    __u8 res2 = 0;
    __u8 res3 = 0;
    int i;

    if(bits >=32)
      return (__u32) -1;
    if(!bits)
      return 0;
    if(bits >= 24)
      {
        bits -= 24;
        res0 = 255;
        res1 = 255;
        res2 = 255;
        for(i=0; i<bits; i++)
          res3 |= 1 << (7-i);
      }
    else
    if(bits >= 16)
      {
        bits -= 16;
        res0 = 255;
        res1 = 255;
        res3 = 0;
        for(i=0; i<bits; i++)
          res2 |= 1 << (7-i);
      }
    else
    if(bits >= 8)
      {
        bits -= 8;
        res0 = 255;
        res2 = 0;
        res3 = 0;
        for(i=0; i<bits; i++)
          res1 |= 1 << (7-i);
      }
    else
      {
        res1 = 0;
        res2 = 0;
        res3 = 0;
        for(i=0; i<bits; i++)
          res0 |= 1 << (7-i);
      }
    res = (res3 << 24) | (res2 << 16) | (res1 << 8) | res0;
    return res;
  }

#ifdef __KERNEL__
/* The lookup data param is always second, so we use it as description here! */
int rsbac_net_compare_data(void * data1, void * data2)
  {
    struct rsbac_net_temp_data_t * temp = data1;
    struct rsbac_net_description_t * desc = data2;

    if(!temp || !desc)
      return 1;
    if(   (temp->address_family != RSBAC_NET_ANY)
       && (temp->address_family != desc->address_family)
      )
      return 1;
    switch(desc->address_family)
      {
        case AF_UNIX:
          if(   (temp->type != RSBAC_NET_ANY)
             && (desc->type != temp->type)
            )
            return 1;
          if(!temp->valid_len)
            return 0;
          if(   !desc->address
             || !desc->address_len
            )
            return 1;
          if(strncmp(desc->address,
                     temp->address,
                     rsbac_min(desc->address_len, temp->valid_len)))
            return 1;
          else
            return 0;

        case AF_INET:
          {
            __u32 mask;

            if(   (temp->type != RSBAC_NET_ANY)
               && (desc->type != temp->type)
              )
              return 1;
            if(   (temp->protocol != RSBAC_NET_ANY)
               && (desc->protocol != temp->protocol)
              )
              return 1;
            if(   (desc->port < temp->min_port)
               || (desc->port > temp->max_port)
              )
              return 1;

            if(   temp->netdev[0]
               && (   !desc->netdev[0]
                   || strncmp(desc->netdev,
                              temp->netdev,
                              RSBAC_IFNAMSIZ)
                  )
              )
              return 1;
            if(!temp->valid_len)
              return 0;
            mask = rsbac_net_make_mask_u32(temp->valid_len);
            if(   !desc->address
               || (( (*(__u32 *)desc->address) & mask ) != ( (*(__u32 *)temp->address) & mask))
              )
              return 1;
            else
              return 0;
          }

        /* Other address families: only socket type checks for now */
        default:
          if(   (temp->type != RSBAC_NET_ANY)
             && (desc->type != temp->type)
            )
            return 1;
          return 0;
      }
    return 1;
  }
#endif
