#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <fltdefs.h>

#define DIR_INPUT    1
#define DIR_OUTPUT   2

#pragma comment(lib, "iphlpapi.lib")

int parseAddress(char *addrStr, 
                 BYTE *addr, 
                 BYTE *mask)
{
    int ip[4], mk[4];
    int num = sscanf(addrStr, "%d.%d.%d.%d/%d.%d.%d.%d", 
        &ip[0], &ip[1], &ip[2], &ip[3],
        &mk[0], &mk[1], &mk[2], &mk[3]);
    if(num != 8)
        return -1;

    for(int i=0; i < 4; i++){
        if(ip[i] < 0 || ip[i] > 255 || mk[i] < 0 || mk[i] > 255)
            return -1;
        addr[i] = ip[i], mask[i] = mk[i];
    }
    return 0;
}

int addFilter(INTERFACE_HANDLE hIf, 
              int flag, 
              DWORD dwProtocol, 
              char *localAddrStr, 
              char *remoteAddrStr)
{
    BYTE localAddrBin[4], remoteAddrBin[4];
    BYTE localMaskBin[4], remoteMaskBin[4];
    parseAddress(localAddrStr,  localAddrBin,  localMaskBin);
    parseAddress(remoteAddrStr, remoteAddrBin, remoteMaskBin);

    PF_FILTER_DESCRIPTOR filter;
    memset(&filter, 0, sizeof(filter));
    filter.dwFilterFlags = FD_FLAGS_NOSYN;
    filter.dwRule        = 0;
    filter.pfatType      = PF_IPV4;
    filter.dwProtocol    = dwProtocol;
    filter.fLateBound    = 0;
    filter.wSrcPort      = ((dwProtocol == FILTER_PROTO_ICMP) 
        ? FILTER_ICMP_TYPE_ANY : FILTER_TCPUDP_PORT_ANY);
    filter.wDstPort      = ((dwProtocol == FILTER_PROTO_ICMP)
        ? FILTER_ICMP_TYPE_ANY : FILTER_TCPUDP_PORT_ANY);
    filter.wSrcPortHighRange = filter.wSrcPort;
    filter.wDstPortHighRange = filter.wDstPort;

    DWORD dwRet;
    if(flag == DIR_INPUT){
        filter.SrcAddr = remoteAddrBin, filter.SrcMask = remoteMaskBin;
        filter.DstAddr = localAddrBin,  filter.DstMask = localMaskBin;
        dwRet = PfAddFiltersToInterface(hIf, 1, &filter, 0, NULL, NULL);
    }else{
        filter.SrcAddr = localAddrBin, filter.SrcMask = localMaskBin;
        filter.DstAddr = remoteAddrBin, filter.DstMask = remoteMaskBin;
        dwRet = PfAddFiltersToInterface(hIf, 0, NULL, 1, &filter, NULL);
    }

    if(dwRet != NO_ERROR){
        printf("addFilter: PfAddFiltersToInterface failed");
        return -1;
    }

    return 0;
}

int main(void)
{
    BYTE localAddr[4] = { 192,168,4,2 };

    INTERFACE_HANDLE hFilterIf;
    DWORD dwRet = PfCreateInterface(
        NULL, PF_ACTION_DROP, PF_ACTION_DROP, FALSE, TRUE, &hFilterIf);
    if(dwRet != NO_ERROR){
        fprintf(stderr, "PfCreateInterface failed");
        return -1;
    }

    addFilter(hFilterIf, DIR_OUTPUT, FILTER_PROTO_ICMP,
        "192.168.4.2/255.255.255.255", "210.198.12.224/255.255.255.255");
    addFilter(hFilterIf, DIR_INPUT,  FILTER_PROTO_ICMP,
        "192.168.4.2/255.255.255.255", "210.198.12.224/255.255.255.255");

    printf("Start Filtering");

    dwRet = PfBindInterfaceToIPAddress(hFilterIf, PF_IPV4, localAddr);
    if(dwRet != NO_ERROR){
        fprintf(stderr, "PfBindInterfaceToIPAddress failed");
        return -1;
    }

    _getch();

    PfUnBindInterface(hFilterIf);
    PfDeleteInterface(hFilterIf);
    return 0;
}
