//---------------------------------------------------------------------------
int nasrg_netlink_send(unsigned char *data_buffer, unsigned int data_length, int destination)
{
  //---------------------------------------------------------------------------
  struct sk_buff *nl_skb;
  struct nlmsghdr *nlh;
  int status;
  // Start debug information
#ifdef NETLINK_DEBUG
  printk("nasrg_netlink_send - begin \n");
#endif

  if (!data_buffer) {
    printk("nasrg_netlink_send - ERROR - input parameter data is NULL \n");
    return(0);
  }

  if (!nas_nl_sk || !nas_rrcnl_sk) {
    printk("nasrg_netlink_send - ERROR - socket is NULL\n");
    return(0);
  }

  // End debug information

  nl_skb = alloc_skb(NLMSG_SPACE(data_length),GFP_ATOMIC);

  if (!nl_skb) {
    printk("nasrg_netlink_send - ERROR - could not allocate skbuffer\n");
    return(0);
  }

  nlh = (struct nlmsghdr *)nl_skb->data;

  //  printk("nasrg_netlink_send Sending %d bytes (%d)\n",data_length,NLMSG_SPACE(data_length));
  skb_put(nl_skb, NLMSG_SPACE(data_length));
  memcpy(NLMSG_DATA(nlh), data_buffer, data_length);
  nlh->nlmsg_len = NLMSG_SPACE(data_length);

  nlh->nlmsg_pid = 0;      /* from kernel */
  NETLINK_CB(nl_skb).pid = 0;

  // destination 0 = PDCP, 1 = RRC
  if (destination== NASNL_DEST_PDCP) {
#ifdef NETLINK_DEBUG
    printk("nasrg_netlink_send - Sending to PDCP - nl_skb %p, nl_sk %p, nlh %p, nlh->nlmsg_len %d\n", nl_skb, nas_nl_sk, nlh, nlh->nlmsg_len);
#ifdef NAS_DEBUG_SEND_DETAIL
    nasrg_TOOL_print_buffer(NLMSG_DATA(nlh),48);
#endif
#endif //DEBUG_NETLINK
    status = netlink_unicast(nas_nl_sk, nl_skb, NL_DEST_PID, MSG_DONTWAIT);
  } else {
#ifdef NAS_DEBUG_RRCNL
    printk("nasrg_netlink_send - Sending to RRC - nl_skb %p, nas_rrcnl_sk %p, nlh %p, nlh->nlmsg_len %d\n", nl_skb, nas_rrcnl_sk, nlh, nlh->nlmsg_len);
    nasrg_TOOL_print_buffer(NLMSG_DATA(nlh),data_length);
#endif //NAS_DEBUG_RRCNL
    status = netlink_unicast(nas_rrcnl_sk, nl_skb, NL_DEST_RRC_PID, MSG_DONTWAIT);
  }

  if (status < 0) {
    printk("nasrg_netlink_send - SEND status is %d\n",status);
    return(0);
  } else {
#ifdef NETLINK_DEBUG
    printk("nasrg_netlink_send - SEND status is %d, data_length %d\n",status, data_length);
#endif
    return data_length;
  }
}
//---------------------------------------------------------------------------
// Search the sending function for IP Packet
void nasrg_CLASS_send(struct sk_buff *skb)
{
  //---------------------------------------------------------------------------
  struct classifier_entity  *pclassifier, *sp;
  uint8_t *protocolh = NULL;
  uint8_t version;
  uint8_t protocol, dscp;
  uint16_t classref;
  struct cx_entity *cx;
  unsigned int i;
#ifdef NAS_DEBUG_CLASS
  char sfct[10], sprotocol[10];
#endif
  struct net_device *dev = gdev;
  unsigned char cx_index,no_connection;
  int addr_type;
  int mbms_ix;
  struct in6_addr masked6_addr;
  struct in_addr  masked_addr;
  // RARP vars
  struct arphdr  *rarp;
  unsigned char  *rarp_ptr;
  /* s for "source", t for "target" */
  __be32 sip, tip;
  unsigned char *sha, *tha;

#ifdef NAS_DEBUG_CLASS
  printk("nasrg_CLASS_send: begin -\n");
#endif

  if (skb==NULL) {
    printk("nasrg_CLASS_send - input parameter skb is NULL \n");
    return;
  }

  //***
#ifdef NAS_DEBUG_SEND
  printk("nasrg_CLASS_send - Received IP packet to transmit, length %d\n", skb->len);
#endif
#ifdef NAS_DEBUG_SEND_DETAIL

  if ((skb->data) != NULL) {
    if (skb->len<150)
      nasrg_TOOL_print_buffer(skb->data,skb->len);
    else
      nasrg_TOOL_print_buffer(skb->data,150);
  }

#endif
  //***
  // find all connections related to socket
  cx_index   = 0;
  no_connection = 1;
  cx = NULL;
#ifdef NAS_DEBUG_CLASS
  printk("nasrg_CLASS_send: [before switch on IP protocol version] \n");
#endif


  // Get mobile connexion entity, protocol and dscp from IP packet
  switch (ntohs(skb->protocol)) {
  case ETH_P_IPV6:
#ifdef NAS_DEBUG_CLASS_DETAIL
    printk("nasrg_CLASS_send : skb->protocol : IPv6 \n");
#endif
    version = NAS_VERSION_6;
    addr_type = NAS_IPV6_ADDR_UNKNOWN;
    protocolh = nasrg_TOOL_get_protocol6(ipv6_hdr(skb), &protocol);
    dscp      = nasrg_TOOL_get_dscp6 (ipv6_hdr(skb));
    cx        = nasrg_CLASS_cx6 (skb, dscp, &addr_type, &cx_index, &mbms_ix);
#ifdef NAS_DEBUG_CLASS_DETAIL
    printk("nasrg_CLASS_send - ETH_P_IPV6 skb %p dscp %d gpriv %p cx_index %p \n",skb, dscp, gpriv, &cx_index);
#endif

    // find in default DSCP a valid classification
    if (cx == NULL) {
      switch (addr_type) {
      case NAS_IPV6_ADDR_MC_SIGNALLING:
      case NAS_IPV6_ADDR_UNICAST:
#ifdef NAS_DEBUG_CLASS_DETAIL
        printk("nasrg_CLASS_send - case NAS_IPV6_ADDR_MC_SIGNALLING | NAS_IPV6_ADDR_UNICAST\n");
#endif //NAS_DEBUG_CLASS

        for (i=0; i<NAS_CX_MAX; i++) {
          pclassifier=(&gpriv->cx[i])->sclassifier[NAS_DSCP_DEFAULT];

          while (pclassifier!=NULL) {
            if ((pclassifier->version == NAS_VERSION_6) || (pclassifier->version == NAS_VERSION_DEFAULT)) {
              // ok found default classifier for this packet
              nasrg_create_mask_ipv6_addr(&masked6_addr, pclassifier->dplen);
              // Modified MW to let everything go (pb with signalling)
              masked6_addr.s6_addr32[0] = 0x00000000;
              masked6_addr.s6_addr32[1] = 0x00000000;
#ifdef NAS_DEBUG_CLASS_DETAIL
              printk("nasrg_CLASS_send - cx %d : DSCP NAS_DSCP_DEFAULT %X:%X:%X:%X:%X:%X:%X:%X\n",i, NIP6ADDR(&(pclassifier->daddr.ipv6)));
#endif //NAS_DEBUG_CLASS

              if (IN6_ARE_ADDR_MASKED_EQUAL(&pclassifier->daddr.ipv6, &ipv6_hdr(skb)->daddr, &masked6_addr)) {
                // then force dscp
                cx = &gpriv->cx[i];
#ifdef NAS_DEBUG_CLASS
                printk("nasrg_CLASS_send - ETH_P_IPV6 FOUND NAS_DSCP_DEFAULT with IN6_ARE_ADDR_MASKED_EQUAL(%d bits)\n",pclassifier->dplen);
#endif
                dscp = NAS_DSCP_DEFAULT;
                break;
              } else {
                if(IN6_IS_ADDR_UNSPECIFIED(&pclassifier->daddr.ipv6)) {
                  cx = &gpriv->cx[i];
#ifdef NAS_DEBUG_CLASS
                  printk("nasrg_CLASS_send - ETH_P_IPV6 FOUND NAS_DSCP_DEFAULT with IN6_IS_ADDR_UNSPECIFIED\n");
#endif
                  dscp = NAS_DSCP_DEFAULT;
                  break;
                }
              }
            }

            pclassifier = pclassifier->next;
          }
        }

        break;

        // MBMS is broken!!!! To be updated (these values will be over-ridden afterwards
      case NAS_IPV6_ADDR_MC_MBMS:
#ifdef NAS_DEBUG_CLASS
        printk("nasrg_CLASS_send - case NAS_IPV6_ADDR_MC_MBMS\n");
#endif //NAS_DEBUG_CLASS
        pclassifier = gpriv->mbmsclassifier[mbms_ix];
        printk("nasrg_CLASS_send: MBMS is broken!!!!\n\n\n");
        sp = gpriv->mbmsclassifier[mbms_ix];

        if (sp!= NULL) {
          classref=sp->classref;
#ifdef NAS_DEBUG_CLASS_DETAIL
          printk("nasrg_CLASS_send: classifier found for multicast service %d \n", mbms_ix);
#endif
        } else {
          printk("nasrg_CLASS_send: No corresponding multicast bearer, so the message is dropped\n");
          return;
        }

        break;

        // should have found a valid classification rule
      case NAS_IPV6_ADDR_UNKNOWN:
      default:
        printk("nasrg_CLASS_send: No corresponding address type\n");
      }
    }

    break;

  case ETH_P_ARP:
#ifdef NAS_DEBUG_CLASS
    printk("nasrg_CLASS_send : skb->protocol : ARP \n");
#endif
    version = NAS_VERSION_4;
    addr_type = NAS_IPV4_ADDR_BROADCAST;
    dscp = 0;
    cx = NULL;
    // Basic sanity checks can be done without the lock
    rarp = (struct arphdr *)skb_network_header(skb);

    if (rarp) {
      if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
        printk("nasrg_CLASS_send: ARP PACKET WRONG ADDR LEN or WRONG ARP HEADER TYPE\n");
        break;
      }
    } else {
      printk("nasrg_CLASS_send: ARP HEADER POINTER IS NULL\n");
      break;
    }

    // If it's not Ethernet, delete it.
    if (rarp->ar_pro != htons(ETH_P_IP)) {
      printk("nasrg_CLASS_send: ARP PACKET PROTOCOL IS NOT ETHERNET\n");
      break;
    }

    rarp_ptr = (unsigned char *) (rarp + 1);
    sha = rarp_ptr;
    rarp_ptr += dev->addr_len;
    memcpy(&sip, rarp_ptr, 4);
    rarp_ptr += 4;
    tha = rarp_ptr;
    rarp_ptr += dev->addr_len;
    memcpy(&tip, rarp_ptr, 4);
#ifdef NAS_DEBUG_CLASS
    printk("nasrg_CLASS_send: ARP DEST IP transport IP = %d.%d.%d.%d\n",NIPADDR(tip));
#endif

    for (i=0; i<NAS_CX_MAX; i++) {
      pclassifier=(&gpriv->cx[i])->sclassifier[NAS_DSCP_DEFAULT];

      while (pclassifier!=NULL) {
        if ((pclassifier->version == NAS_VERSION_4) || (pclassifier->version == NAS_VERSION_DEFAULT)) {
          // ok found default classifier for this packet
          nasrg_create_mask_ipv4_addr(&masked_addr, pclassifier->dplen);
#ifdef NAS_DEBUG_CLASS
          printk("nasrg_CLASS_send: MASK = %d.%d.%d.%d\n",NIPADDR(masked_addr.s_addr));
#endif

          //
          if (IN_ARE_ADDR_MASKED_EQUAL(&pclassifier->daddr.ipv4, &tip, &masked_addr.s_addr)) {
            // then force dscp
            cx = &gpriv->cx[i];
#ifdef NAS_DEBUG_CLASS
            printk("nasrg_CLASS_send: ETH_P_ARP FOUND NAS_DSCP_DEFAULT with IN_ARE_ADDR_MASKED_EQUAL(%d bits)\n", pclassifier->dplen);
#endif
            dscp = NAS_DSCP_DEFAULT;
            break;
          } else {
            if (INADDR_ANY == pclassifier->daddr.ipv4) {
              cx = &gpriv->cx[i];
#ifdef NAS_DEBUG_CLASS
              printk("nasrg_CLASS_send: ETH_P_ARP FOUND NAS_DSCP_DEFAULT with INADDR_ANY\n");
#endif
              dscp = NAS_DSCP_DEFAULT;
              break;
            }
          }
        }

        pclassifier = pclassifier->next;
      }
    }

    break;

  case ETH_P_IP:
#ifdef NAS_DEBUG_CLASS_DETAIL
    printk("nasrg_CLASS_send : skb->protocol : IPv4 \n");
#endif
    version   = NAS_VERSION_4;
    addr_type = NAS_IPV4_ADDR_UNKNOWN;
    dscp      = nasrg_TOOL_get_dscp4((struct iphdr *)(skb_network_header(skb)));
    cx        = nasrg_CLASS_cx4(skb, dscp, &addr_type, &cx_index);
    protocolh = nasrg_TOOL_get_protocol4((struct iphdr *)(skb_network_header(skb)), &protocol);

    // find in default DSCP a valid classification
    if (cx == NULL) {
      switch (addr_type) {
      case NAS_IPV4_ADDR_MC_SIGNALLING:
      case NAS_IPV4_ADDR_UNICAST:
      case NAS_IPV4_ADDR_BROADCAST:
        for (i=0; i<NAS_CX_MAX; i++) {
          pclassifier=(&gpriv->cx[i])->sclassifier[NAS_DSCP_DEFAULT];

          while (pclassifier != NULL) {
            if ((pclassifier->version == NAS_VERSION_4) || (pclassifier->version == NAS_VERSION_DEFAULT)) {
              // ok found default classifier for this packet
              nasrg_create_mask_ipv4_addr(&masked_addr, pclassifier->dplen);
#ifdef NAS_DEBUG_CLASS_DETAIL
              printk("nasrg_CLASS_send : MASK = %d.%d.%d.%d\n", NIPADDR(masked_addr.s_addr));
#endif

              if (IN_ARE_ADDR_MASKED_EQUAL(&pclassifier->daddr.ipv4, &ip_hdr(skb)->daddr, &masked_addr.s_addr)) {
                // then force dscp
                cx = &gpriv->cx[i];
#ifdef NAS_DEBUG_CLASS
                printk("nasrg_CLASS_send : ETH_P_IP FOUND NAS_DSCP_DEFAULT with IN_ARE_ADDR_MASKED_EQUAL(%d bits)\n",pclassifier->dplen);
#endif
                dscp = NAS_DSCP_DEFAULT;
                break;
              } else {
                if(INADDR_ANY == pclassifier->daddr.ipv4) {
                  cx = &gpriv->cx[i];
#ifdef NAS_DEBUG_CLASS
                  printk("nasrg_CLASS_send : ETH_P_IP FOUND NAS_DSCP_DEFAULT with INADDR_ANY\n");
#endif
                  dscp = NAS_DSCP_DEFAULT;
                  break;
                }
              }
            }

            pclassifier = pclassifier->next;
          }
        }

        break;

        // should have found a valid classification rule
      case NAS_IPV4_ADDR_UNKNOWN:
      default:
        printk("nasrg_CLASS_send: No corresponding address type\n");
      }
    }

#ifdef NAS_DEBUG_CLASS

    if (cx)
      printk("nasrg_CLASS_send: ETH_P_IP Received IPv4 packet (%02X), dscp = %d, cx = %d\n",ntohs(skb->protocol),dscp,cx->lcr);
    else
      printk("nasrg_CLASS_send: ETH_P_IP Received IPv4 packet (%02X), dscp = %d, No valid connection\n",ntohs(skb->protocol),dscp);

#endif
    break;

  default:
    printk("nasrg_CLASS_send: Unknown IP version protocol\n");
    version = 0;
    return;
  }

#ifdef NAS_DEBUG_CLASS_DETAIL
  printk("nasrg_CLASS_send: [before if (cx != NULL)]\n");
#endif

  //Next lines bypass classifiers to test the netlink socket
  //#define DEBUG_NETLINKRG_TEST
#ifdef DEBUG_NETLINKRG_TEST
  nasrg_COMMON_QOS_send_test_netlink(skb);
  return;
#endif

  // If a valid connection for the DSCP/EXP with destination address
  // is found scan all protocol-based classification rules
  if (cx != NULL) {
    classref = 0;
    sp = NULL;

    if (cx->state!=NAS_CX_DCH) {
#ifdef NAS_DEBUG_CLASS
      printk("nasrg_CLASS_send: UE not connected, in state %d. Packet is dropped\n",cx->state);
#endif
      return;
    }

    if (addr_type==NAS_IPV6_ADDR_MC_MBMS) {
      sp = gpriv->mbmsclassifier[mbms_ix];

      if (sp!= NULL) {
        classref=sp->classref;
#ifdef NAS_DEBUG_CLASS
        printk("nasrg_CLASS_send: classifier found for multicast index %d, service %d\n", mbms_ix, gpriv->mbms_rb[mbms_ix].cnxid);
#endif
      } else {
        // Temp MEDIEVAL : use default classifier
        sp = cx->sclassifier[NAS_DSCP_DEFAULT];

        if (sp!= NULL) {
          classref=sp->classref;
#ifdef NAS_DEBUG_CLASS
          printk("nasrg_CLASS_send: classifier for multicast service %d replaced by default %d\n", mbms_ix, classref);
#endif
#ifdef NAS_AUTO_MBMS
          nasrg_ASCTL_start_default_mbms_service();
#endif
        } else {
          printk("nasrg_CLASS_send: No corresponding multicast bearer, so the message is dropped\n");
          return;
        }
      }
    } else {
#ifdef NAS_DEBUG_CLASS_DETAIL
      printk("nasrg_CLASS_send: DSCP %d version %d: looking for classifier entry\n",dscp, version);
#endif

      for (pclassifier=cx->sclassifier[dscp]; pclassifier!=NULL; pclassifier=pclassifier->next) {
#ifdef NAS_DEBUG_CLASS_DETAIL
        printk("nasrg_CLASS_send: DSCP %d p->classref=%d,p->protocol=%d,p->version=%d\n",dscp,pclassifier->classref,pclassifier->protocol,pclassifier->version);
#endif

        // normal rule checks that network protocol version matches
        if ((pclassifier->version == version)  || (pclassifier->version == NAS_VERSION_DEFAULT)) {
          //printk("nasrg_CLASS_send: IP version are equals\n");
          sp=pclassifier;
          classref=sp->classref;
#ifdef NAS_DEBUG_CLASS_DETAIL
          printk("nasrg_CLASS_send: classifier found for dscp %u \n", dscp);
#endif
          break;
        }
      }
    }

    if (sp!=NULL) {
#ifdef NAS_DEBUG_CLASS

      //char sfct[10], sprotocol[10];
      // classifier entity found. Print its parameters
      if (sp->fct==nasrg_COMMON_QOS_send)
        strcpy(sfct, "data xfer");

      if (sp->fct==nasrg_CTL_send)
        strcpy(sfct, "iocontrol");

      if (sp->fct==nasrg_COMMON_del_send)
        strcpy(sfct, "delete");

      if (sp->fct==nasrg_ASCTL_DC_send_sig_data_request)
        strcpy(sfct, "DC-SAP");

      switch(protocol) {
      case NAS_PROTOCOL_UDP:
        strcpy(sprotocol, "udp");
        printk("udp packet\n");
        break;

      case NAS_PROTOCOL_TCP:
        strcpy(sprotocol, "tcp");
        printk("tcp packet\n");
        break;

      case NAS_PROTOCOL_ICMP4:
        strcpy(sprotocol, "icmp4");
        printk("icmp4 packet\n");
        break;

      case NAS_PROTOCOL_ICMP6:
        strcpy(sprotocol, "icmp6");
        nasrg_TOOL_pk_icmp6((struct icmp6hdr*)protocolh);
        break;

      default:
        strcpy(sprotocol, "other L4");
        break;
      }

      printk("nasrg_CLASS_send: (dscp %u, %s) received, (classref %u, fct %s, drb_id %u) classifier rule\n",
             dscp, sprotocol, sp->classref, sfct, sp->rab_id);
#endif

      //forward packet to the correct entity
      if (sp->fct!=NULL) {
        sp->fct(skb, cx, sp);
      } else {
        printk("\n\nnasrg_CLASS_send: ERROR : CLASSIFIER FUNCTION IS NULL\n\n");
      }

      no_connection = 0;
      // end : if classifier entry match found
    } else {
      printk("nasrg_CLASS_send: no corresponding item in the classifier list, so the message is dropped\n");
      printk("nasrg_CLASS_send: packet parameters: dscp %u, %s\n", dscp, sprotocol);
      nasrg_COMMON_del_send(skb, cx, NULL);  // Note MW: LG has commented this line. Why?
    }
  }   // if connection found

#ifdef NAS_DEBUG_CLASS

  if (no_connection == 1) {
    printk("nasrg_CLASS_send: no corresponding connection, so the message is dropped\n");
  }

#endif
#ifdef NAS_DEBUG_CLASS_DETAIL
  printk("nasrg_CLASS_send: end\n");
#endif
}