/*
 * simco_core_ext.c
 *
 * Copyright (C) 2005, Ranch Networks, Inc.
 *
 * 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.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * Author : Srivatsa Chivukula <srivatsa@ranchnetworks.com>
 *
 */

#include "simco_core_ext.h"
#include "simco_client_ext.h"
#include "simco_defs.h"
#include "simco_defs_ext.h"


/**
 * Parses the extended simco packet passed in \a pdu and fills corresponding 
 * fields in \a sep.
 * @param sep pointer to the extented simco structure which will be filled.
 * @param pdu pointer to the extended simco packet.
 * @param size size of the extended simco packet.
 */
void parseSIMCOExtPdu(simcoExtPacket_t *sep, char *pdu, int size)
{
  short attrlen;
  char *parse_ptr = pdu;
  if (*parse_ptr == BSC_NEG_RPL_MSG)
    {
      sep->status = *(parse_ptr+1);
      return;
    }
  else
    sep->status = 0;
  parse_ptr += 8;
  size -= 8;
  /* memset((void*)sep, 0, sizeof(simcoExtPacket_t)); */
  while (size)
    {
      if (ntohs(*(short*)parse_ptr) == PAY_PRI_ATT)
	{
	  attrlen = ntohs(*(short*)(parse_ptr+2));
	  if (size < attrlen) break;
	  sep->ruleId = ntohl(*(int*)(parse_ptr+4));
	}
      else if (ntohs(*(short*)parse_ptr) == PAY_PRL_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
          sep->lifetime = ntohl(*(int*)(parse_ptr+4));
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_PRO_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FRC_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
          sep->action = *(parse_ptr+4);
          sep->syslog = *(parse_ptr+5);
          sep->srcZoneId = *(parse_ptr+6);
          sep->dstZoneId = *(parse_ptr+7);
          sep->protocol = *(parse_ptr+8);
          sep->tos = *(parse_ptr+9);
          sep->ipfrag_selfdst = *(parse_ptr+10);
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FIF_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FTF_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
	  sep->ipfrag_selfdst = ntohl(*(int*)(parse_ptr+4));
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FIO_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
	  sep->tcp_flags_options = ntohl(*(int*)(parse_ptr+4));
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FPR_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
          sep->packet_rate = ntohl(*(int*)(parse_ptr+4));
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FRJ_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
          sep->reject_code = ntohs(*(short*)(parse_ptr+4));
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_ADD_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
          if (attrlen == 12)
            {
              if (*(parse_ptr+7) == ATA_A0_FLG)
                {
                  sep->internal_ip_mask = (int)*(parse_ptr+5);
                  sep->protocol = (int)*(parse_ptr+6);
                  sep->internal_port = ntohs(*(short*)(parse_ptr+8));
                  sep->internal_port_range = ntohs(*(short*)(parse_ptr+10));
                  ip_ntoa((unsigned int)(*(int*)(parse_ptr+12)), sep->internal_ip);
                }
              else if (*(parse_ptr+7) == ATA_A3_FLG)
                {
                  sep->external_ip_mask = (int)*(parse_ptr+5);
                  sep->protocol = (int)*(parse_ptr+6);
                  sep->external_port = ntohs(*(short*)(parse_ptr+8));
                  sep->external_port_range = ntohs(*(short*)(parse_ptr+10));
                  ip_ntoa((unsigned int)(*(int*)(parse_ptr+12)), sep->external_ip);
                }
            }
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FIP_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
	  ip_ntoa((unsigned int)(*(int*)(parse_ptr+4)), sep->copy_forward_ip);
        }
      else if (ntohs(*(short*)parse_ptr) == PAY_FEC_ATT)
        {
          attrlen = ntohs(*(short*)(parse_ptr+2));
          if (size < attrlen) break;
	  sep->ext_capabilities = ntohl(*(int*)(parse_ptr+4));
	  sep->lifetime = ntohl(*(int*)(parse_ptr+8));
        }
      else
        {
          if (debug_midbox)
	      printf("%s:%d %s() Midcom: Received bad packet %hx, abort parsing !!!\n", __FILE__, __LINE__, __FUNCTION__, ntohs(*(short*)parse_ptr));
          break;
        }
      size -= (attrlen + 4);
      parse_ptr += (attrlen + 4);
    }
  return;
}

int makeFirewallRuleCreateRequest(char *request, const simcoExtPacket_t *sep)
{
  int index = 0;
  memset((char*)&request[0], 0, sizeof(request));
  SIMCO_CHAR_INSERT(request, index, BSC_REQ_MSG);
  SIMCO_CHAR_INSERT(request, index, SUB_FRC_MSG);
  SIMCO_SHORT_INSERT(request, index, 0);
  SIMCO_INT_INSERT(request, index, 0);
  SIMCO_SHORT_INSERT(request, index, PAY_FRC_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0008);
  SIMCO_CHAR_INSERT(request, index, sep->action);
  SIMCO_CHAR_INSERT(request, index, sep->syslog);
  SIMCO_CHAR_INSERT(request, index, sep->srcZoneId);
  SIMCO_CHAR_INSERT(request, index, sep->dstZoneId);
  SIMCO_CHAR_INSERT(request, index, sep->protocol);
  SIMCO_CHAR_INSERT(request, index, sep->tos);
  SIMCO_CHAR_INSERT(request, index, sep->ipfrag_selfdst);
  SIMCO_CHAR_INSERT(request, index, 0);
  SIMCO_SHORT_INSERT(request, index, PAY_PRL_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->lifetime);
  SIMCO_SHORT_INSERT(request, index, PAY_ADD_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x000c);
  SIMCO_CHAR_INSERT(request, index, 0x01);
  if ((sep->internal_ip_mask <= 0) || (sep->internal_ip_mask > 0x20))
    {
      SIMCO_CHAR_INSERT(request, index, 0x20);
    }
  else
    {
      SIMCO_CHAR_INSERT(request, index, sep->internal_ip_mask);
    }
  SIMCO_CHAR_INSERT(request, index, sep->protocol);
  SIMCO_CHAR_INSERT(request, index, 0x00);
  SIMCO_SHORT_INSERT(request, index, sep->internal_port);
  if ((sep->internal_port != 0) && (sep->internal_port_range == 0))
    {
      SIMCO_SHORT_INSERT(request, index, 0x0001);
    }
  else
    {
      SIMCO_SHORT_INSERT(request, index, sep->internal_port_range);
    }
  SIMCO_INT_INSERT(request, index, htonl(inet_addr(sep->internal_ip)));
  SIMCO_SHORT_INSERT(request, index, PAY_ADD_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x000c);
  SIMCO_CHAR_INSERT(request, index, 0x01);
  if ((sep->external_ip_mask <= 0) || (sep->external_ip_mask > 0x20))
    {
      SIMCO_CHAR_INSERT(request, index, 0x20);
    }
  else
    {
      SIMCO_CHAR_INSERT(request, index, sep->external_ip_mask);
    }
  SIMCO_CHAR_INSERT(request, index, sep->protocol);
  SIMCO_CHAR_INSERT(request, index, 0x03);
  SIMCO_SHORT_INSERT(request, index, sep->external_port);
  if ((sep->external_port != 0) && (sep->external_port_range == 0))
    {
      SIMCO_SHORT_INSERT(request, index, 0x0001);
    }
  else
    {
      SIMCO_SHORT_INSERT(request, index, sep->external_port_range);
    }
  SIMCO_INT_INSERT(request, index, htonl(inet_addr(sep->external_ip)));
  SIMCO_SHORT_INSERT(request, index, PAY_FIF_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->ip_options);
  SIMCO_SHORT_INSERT(request, index, PAY_FTF_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->tcp_flags_options);
  SIMCO_SHORT_INSERT(request, index, PAY_FIO_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->icmp_type);
  SIMCO_SHORT_INSERT(request, index, PAY_FPR_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->packet_rate);
  SIMCO_SHORT_INSERT(request, index, PAY_FRJ_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->reject_code);
  SIMCO_SHORT_INSERT(request, index, PAY_FIP_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, htonl(inet_addr(sep->copy_forward_ip)));
  SIMCO_SIZE_INSERT(request, index-8);
  return index;
}

int makeFirewallRuleDeleteRequest(char *request, const simcoExtPacket_t *sep)
{
  int index = 0;
  memset((char*)&request[0], 0, sizeof(request));
  SIMCO_CHAR_INSERT(request, index, BSC_REQ_MSG);
  SIMCO_CHAR_INSERT(request, index, SUB_FRD_MSG);
  SIMCO_SHORT_INSERT(request, index, 0);
  SIMCO_INT_INSERT(request, index, 0);
  SIMCO_SHORT_INSERT(request, index, PAY_PRI_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->ruleId);
  SIMCO_SIZE_INSERT(request, index-8);
  return index;
}

int makeFirewallRuleStatusRequest(char *request, const simcoExtPacket_t *sep)
{
  int index = 0;
  memset((char*)&request[0], 0, sizeof(request));
  SIMCO_CHAR_INSERT(request, index, BSC_REQ_MSG);
  SIMCO_CHAR_INSERT(request, index, SUB_FRS_MSG);
  SIMCO_SHORT_INSERT(request, index, 0);
  SIMCO_INT_INSERT(request, index, 0);
  SIMCO_SHORT_INSERT(request, index, PAY_PRI_ATT);
  SIMCO_SHORT_INSERT(request, index, 0x0004);
  SIMCO_INT_INSERT(request, index, sep->ruleId);
  SIMCO_SIZE_INSERT(request, index-8);
  return index;
}

int makeFirewallRuleListRequest(char *request)
{
  int index = 0;
  SIMCO_CHAR_INSERT(request, index, BSC_REQ_MSG);
  SIMCO_CHAR_INSERT(request, index, SUB_FRL_MSG);
  SIMCO_SHORT_INSERT(request, index, 0);
  SIMCO_INT_INSERT(request, index, 0);
  SIMCO_SIZE_INSERT(request, index-8);
  return index;
}
