Implementing Filtering and Qos in WinpkFilter

Home Forums Discussions Support Implementing Filtering and Qos in WinpkFilter

Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • #4907
    joga
    Participant

      Hi,
      Here is the scenario:
      We have develop is windows client using winpkfilter 2.4, which provides the following functionality:
      1. Port based firewall rules.
      2. Disallow any LAN traffic based on Ethernet address of the Gateway, i.e. allow traffic only to the gateway.
      3. Number of packets per second for a protocol (i.e. IP, ARP, RARP etc.)
      4. Byte based Qos control.

      In normal situations, it works fine, everything is as expected. But in the case of flooding, it failes. It does not send even the actual packets.

      Implementation:
      Application is written in VC++ 6 using the C++ ndisapi. I have a thread which reads the packets from the queue, does the decision. At the end it purges the queue.

      I have the following questions:

      1. What happen to the packets, which are received while I am reading from the queue? And if they are added to the queue, what happens to them when I purge the queue?

      2. I have posted my code also. Can somebody point out if there is anything I am doing wrong. I required I can post the fill source code?

      3. Is it possible to achive what I mentioned in the scenario using winpkfilter at all? Or is there some other way I can achive this using winpkfilter or some other tool/library ?

      4. Am I doing too much processing while reading from the queue? I tested it on a pc which was doing lot of netbios flooding. It was stopping that, but it was not sending the valid packets to my gateway (ping to gateway).

      Regards:

      Joga Singh

      DWORD WINAPI CaptureFunc( LPVOID lpParam )
      {
      UINT counter = 0;
      ether_header* pEthHeader = NULL;
      iphdr_t* ip_hdr=NULL;
      tcphdr* tcp_hdr=NULL;
      udphdr* udp_hdr=NULL;
      ipportshdr* ports_hdr=NULL;
      unsigned char *raw_packet;
      UINT32 hlen; /* ip header length */

      TCP_AdapterList AdList;
      DWORD iIndex;
      CNdisApi api;
      ETH_REQUEST Request;
      INTERMEDIATE_BUFFER PacketBuffer;
      HANDLE hEvent;
      UINT *packet_counter;

      char temp_str[25];
      packet_limit_t *packet_limit;

      char smac[25], dmac[25], sip[25],dip[25],proto[25],direction[4];
      int sport,dport;
      short protocolIndex,isDrop;

      iIndex = adapterIndex-1;

      if(!api.IsDriverLoaded())
      {
      setLastError("Driver not loaded");
      return 0;
      }

      api.GetTcpipBoundAdaptersInfo ( &AdList );

      if ( iIndex +1 > AdList.m_nAdapterCount )
      {
      setLastError("Wrong network adapter");
      return 0;
      }
      MACSTR(local_mac_str,AdList.m_czPermanentAddress[iIndex]);

      /*AdList.m_czPermanentAddress[0],
      AdList.m_czPermanentAddress
      [1],
      AdList.m_czPermanentAddress
      [2],
      AdList.m_czPermanentAddress
      [3],
      AdList.m_czPermanentAddress
      [4],
      AdList.m_czPermanentAddress
      [5]
      */

      ADAPTER_MODE Mode;

      Mode.dwFlags = MSTCP_FLAG_SENT_TUNNEL;
      Mode.hAdapterHandle = (HANDLE)AdList.m_nAdapterHandle[iIndex];

      // Create notification event
      hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

      // Set event for helper driver
      if ((!hEvent)||(!api.SetPacketEvent((HANDLE)AdList.m_nAdapterHandle[iIndex], hEvent)))
      {
      setLastError("Failed to create notification event or set it for driver.");
      return 0;
      }

      ZeroMemory (&Request, sizeof(ETH_REQUEST) );
      ZeroMemory ( &PacketBuffer, sizeof(INTERMEDIATE_BUFFER) );
      Request.EthPacket.Buffer = &PacketBuffer;
      Request.hAdapterHandle = (HANDLE)AdList.m_nAdapterHandle[iIndex];

      api.SetAdapterMode(&Mode);
      runningStatus=1;
      DEBUG("Filter started");
      while (!threadExit)
      {
      WaitForSingleObject ( hEvent, INFINITE );
      ResetEvent(hEvent);
      while(api.ReadPacket(&Request))
      {
      pEthHeader = (ether_header*)PacketBuffer.m_IBuffer;
      MACSTR(smac,pEthHeader->h_source);
      MACSTR(dmac,pEthHeader->h_dest);
      WaitForSingleObject(mainMutex,INFINITE);
      isDrop=0;
      protocolIndex=getProtocolIndex(&pEthHeader->h_proto);

      if (protocolIndex ==-1) {
      DEBUG3("Protocol Unknow: %d %s [%d]",ntohs(pEthHeader->h_proto),dmac,PacketBuffer.m_Length);
      isDrop=1;
      goto do_decesion;
      }

      //if local lan is not allowed and destination mac is not the gateway mac. drop the packet
      // if (!allowLocalLan && (memcmp(pEthHeader->h_dest,&gateway_mac,sizeof(struct ether_addr)) != 0)) {
      if (!allowLocalLan && (protocolIndex==2 && strcmp(dmac,"FF:FF:FF:FF:FF:FF") !=0) && (strcmp(dmac,gateway_mac_str) != 0)) {
      DEBUG3("LAN packets dropped: %d %s [%d]",ntohs(pEthHeader->h_proto),dmac,PacketBuffer.m_Length);
      isDrop=1;
      goto do_decesion;
      }
      //stop flodding. don't allow fake source mac address
      // if (memcmp(pEthHeader->h_source,&local_mac,sizeof(struct ether_addr)) != 0) {
      if (strcmp(smac,local_mac_str) != 0) {
      DEBUG3("Source MAC based flood: %d %s [%d]",ntohs(pEthHeader->h_proto),dmac,PacketBuffer.m_Length);
      isDrop=1;
      goto do_decesion;
      }

      strcpy(proto,protocol_names[protocolIndex]);

      //get the lock to the global variables

      packet_counter=&packet_counters[protocolIndex-1];
      packet_limit=&packet_limits[protocolIndex-1];

      // DEBUG3("protocol=%d,counter=%d, limit=%d",protocolIndex, *packet_counter,packet_limit->num);
      //most probable case. First check the packet count limit
      if (packet_limit->num != -1) {
      // DEBUG("Checking packet limit, Counter=%d",packet_counter);
      if (*packet_counter >= packet_limit->num) {
      DEBUG3("Packet Qos: %d %s [%d]",protocolIndex,dmac,PacketBuffer.m_Length);
      isDrop=1;
      goto do_decesion;
      }
      }
      //now check if the byte limits are reached

      // DEBUG("Limit=%lld, Current=%Fd",byteLimit,stat_ps.bytesSent);
      if (byteLimit != -1 && stat_ps.bytesSent >= byteLimit){
      DEBUG3("Byte Qos Reached: %d %s [%d]",protocolIndex,dmac,PacketBuffer.m_Length);
      isDrop=1;
      goto do_decesion;
      }


      //basic firewalling
      if (protocolIndex==1) { //IP Protocol. Check for the ports and IPs
      raw_packet=(unsigned char *)pEthHeader+14;// PacketBuffer.m_IBuffer+14;
      ip_hdr = (iphdr_t *)raw_packet;
      hlen = IP_HLEN(ip_hdr) << 2;
      // printf("hlen=%d.vhl=%02xn",hlen,ip_hdr->ip_vhl);

      ports_hdr=(ipportshdr *)raw_packet+hlen;

      //search dest port in port list
      if (ip_hdr->ip_p==IPPROTO_TCP || ip_hdr->ip_p==IPPROTO_UDP) {
      sprintf(temp_str,"%d",ntohs(ip_hdr->th_dport));
      if (usePortBlackList) {
      //DEBUG("Dest Port=%s, Sport=%d",temp_str,ntohs(ports_hdr->th_sport));
      printf("Dest Port=%s, Sport=%dn",temp_str,ntohs(ports_hdr->th_sport));
      //black list is used. search the port in the black list
      if (hash_find(portList,temp_str,NULL)==0) { //found in black list
      DEBUG3("Port %s is black listed: %s [%d]",temp_str,dmac,PacketBuffer.m_Length);
      isDrop=1;
      goto do_decesion;
      }
      }else { //white list is used. allow only if found in the white list
      if (hash_find(portList,temp_str,NULL) !=0) { //not found in white list
      DEBUG3("Port not in white list: %d %s [%d]",protocolIndex,dmac,PacketBuffer.m_Length);
      goto do_decesion;
      }
      }
      }
      }

      do_decesion:
      if (!isDrop) {
      *packet_counter +=1;
      stat_ps.packetsSent++;
      stat_ps.bytesSent +=PacketBuffer.m_Length;
      //TODO: do the filtering
      api.SendPacketToAdapter(&Request);
      }else{
      // fprintf(stderr,"Dropped: %s %s %s > %s [%d]",direction,proto,smac,dmac,PacketBuffer.m_Length);
      total_stat.packetsDropped++;
      total_stat.bytesDropped +=PacketBuffer.m_Length;
      }

      ReleaseMutex(mainMutex);
      }
      api.FlushAdapterPacketQueue (AdList.m_nAdapterHandle[iIndex]);
      }
      DEBUG("Filter exited");

      //release the interface
      Mode.dwFlags = 0;
      Mode.hAdapterHandle = (HANDLE)AdList.m_nAdapterHandle[iIndex];

      // Set NULL event to release previously set event object
      api.SetPacketEvent(AdList.m_nAdapterHandle[iIndex], NULL);

      // Close Event
      if (hEvent)
      CloseHandle ( hEvent );

      // Set default adapter mode
      api.SetAdapterMode(&Mode);

      // Empty adapter packets queue
      api.FlushAdapterPacketQueue (AdList.m_nAdapterHandle[iIndex]);
      runningStatus=0;
      return 0;
      }

      #5705
      Vadim Smirnov
      Keymaster

        1. What happen to the packets, which are received while I am reading from the queue? And if they are added to the queue, what happens to them when I purge the queue?

        These packets are added to the queue until free intermediate buffers are avalaible, after this new packets are dropped. If you call FlushAdapterPacketQueue then all queued packets for the given adapter are deleted from the queue and associated resources are released.

        2. I have posted my code also. Can somebody point out if there is anything I am doing wrong. I required I can post the fill source code?

        Hmm, I would advise to remove all “printf” output from the packet processing code because it has a serious perfomance impact. Also, if your system is loadad with something else during packet processing I would recommend to increase packet processing code priority. If all above won’t improve the situation then I recommend to profile whole your application with one of the profilers available on the market (COmpuware TrueTime an example). If achieved perfomance is not enough yet, then the only thing to do is moving all your code into the kernel (direct integration into the WinpkFilter drivers).

        3. Is it possible to achive what I mentioned in the scenario using winpkfilter at all? Or is there some other way I can achive this using winpkfilter or some other tool/library ?

        Yes, everything described can be realized using WinpkFilter. Your scenario is not unique. I’ve been working about similar solutions before. Btw, since you process only outgoing packet in the code you have provided, then what flooding do you mean? Do you run some sort of local traffic generator (UDP sender or something)? If yes, then please take into account that this application also decrease overall perfomance of your filter, because it also neeeds processor time.

        4. Am I doing too much processing while reading from the queue? I tested it on a pc which was doing lot of netbios flooding. It was stopping that, but it was not sending the valid packets to my gateway (ping to gateway).

        I don’t think the code below does too much processing.

        Hope it helps…

      Viewing 2 posts - 1 through 2 (of 2 total)
      • You must be logged in to reply to this topic.