# Network filter driver



## lopez86100 (Feb 2, 2012)

Hi!

I'm writing a network filter driver for (mac OSX) but I think it's similar to free bsd FreeBSD. I need to get process id of the process that made connection inside function myipfilter_input_redirect.

Is that possible?


```
#include <mach/mach_types.h>
#include <sys/kernel_types.h>
#include <sys/systm.h>
#include <sys/kpi_mbuf.h>
#include <i386/endian.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/kpi_ipfilter.h>

kern_return_t MyIPFilter_start (kmod_info_t * ki, void * d);
kern_return_t MyIPFilter_stop (kmod_info_t * ki, void * d);

enum {
    kMyFiltDirIn,
    kMyFiltDirOut,
    kMyFiltNumDirs
};

struct myfilter_stats
{
    unsigned long udp_packets[kMyFiltNumDirs];
    unsigned long tcp_packets[kMyFiltNumDirs];
    unsigned long icmp_packets[kMyFiltNumDirs];
    unsigned long other_packets[kMyFiltNumDirs];
};

static struct myfilter_stats g_filter_stats;
static ipfilter_t g_filter_ref;
static boolean_t g_filter_registered = FALSE, g_filter_registered2 = FALSE;
static boolean_t g_filter_detached = FALSE;
static boolean_t redirecthttps = FALSE, redirecthttp = FALSE;



static void log_ip_packet(mbuf_t *data, int dir)
{
    char src[32], dst[32];
    struct ip *ip = (struct ip*)mbuf_data(*data);
    
    if (ip->ip_v != 4)
        return;
    
    bzero(src, sizeof(src));
    bzero(dst, sizeof(dst));
    inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
    inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
    
    switch (ip->ip_p) {
        case IPPROTO_TCP:
            printf("TCP: ");
            g_filter_stats.tcp_packets[dir]++;
            break;
        case IPPROTO_UDP:
            printf("UDP: ");
            g_filter_stats.udp_packets[dir]++;
            break;
        case IPPROTO_ICMP:
            printf("ICMP: ");
            g_filter_stats.icmp_packets[dir]++;
        default:
            printf("OTHER: ");
            g_filter_stats.other_packets[dir]++;
            break;
    }
    
    printf("%s -> %s\n", src, dst);
}

static void myipfilter_update_cksum(mbuf_t data)
{
    u_int16_t ip_sum;
    u_int16_t tsum;
    struct tcphdr* tcp;
    struct udphdr* udp;
    
    unsigned char *ptr = (unsigned char*)mbuf_data(data);
    
    struct ip *ip = (struct ip*)ptr;
    if (ip->ip_v != 4)
        return;
    
    ip->ip_sum = 0;
    mbuf_inet_cksum(data, 0, 0, ip->ip_hl << 2, &ip_sum); // ip sum
    
    ip->ip_sum = ip_sum;
    switch (ip->ip_p) {
        case IPPROTO_TCP:
            tcp = (struct tcphdr*)(ptr + (ip->ip_hl << 2));
            tcp->th_sum = 0;
            mbuf_inet_cksum(data, IPPROTO_TCP, ip->ip_hl << 2, ntohs(ip->ip_len) - (ip->ip_hl << 2), &tsum);
            tcp->th_sum = tsum;
            break;
        case IPPROTO_UDP:
            udp = (struct udphdr*)(ptr + (ip->ip_hl << 2));
            udp->uh_sum = 0;
            mbuf_inet_cksum(data, IPPROTO_UDP, ip->ip_hl << 2, ntohs(ip->ip_len) - (ip->ip_hl << 2), &tsum);
            udp->uh_sum = tsum;
            break;
        default:
            break;
    }
    
    mbuf_clear_csum_performed(data); // Needed?
}

 
 //Uncomment to enable re-direction of IP packets to another address.




static errno_t myipfilter_output_redirect(void *cookie, mbuf_t *data, ipf_pktopts_t options)
{
    struct in_addr addr_old;
    struct in_addr addr_new;
    int ret;
    
    struct ip *ip = (struct ip*)mbuf_data(*data);
    if (ip->ip_v != 4)
        return 0;
    
    addr_old.s_addr = htonl(134744072); // 8.8.8.8 
    addr_new.s_addr = htonl(167837964); // 10.1.1.12 (replace with own IP)
    
    // redirect packets to 8.8.8.8 to the IP address 10.1.1.12.
    if (ip->ip_dst.s_addr == addr_old.s_addr)
    {
        ip->ip_dst = addr_new;
        myipfilter_update_cksum(*data);
        ret = ipf_inject_output(*data, g_filter_ref, options);
        return ret == 0 ? EJUSTRETURN : ret;
    }
    return 0;
}

static errno_t myipfilter_input_redirect(void *cookie, mbuf_t *data, int offset, u_int8_t protocol)
{
    struct in_addr addr_old;
    struct in_addr addr_new;
    int ret;
    
    struct ip *ip = (struct ip*)mbuf_data(*data);
    if (ip->ip_v != 4)
        return 0;
    addr_new.s_addr = htonl(134744072); // 8.8.8.8
    addr_old.s_addr = htonl(167837964); // 10.1.1.12 (replace with own IP)
    
    // redirect packets to 8.8.8.8 to the IP address 10.1.1.12.
    if (ip->ip_src.s_addr == addr_old.s_addr)
    {
        ip->ip_src = addr_new;
        // re-inject modified packet.
        myipfilter_update_cksum(*data);
        ret = ipf_inject_input(*data, g_filter_ref);
        printf("redirecting input packet\n");
        return ret == 0 ? EJUSTRETURN : ret;
    }
    return 0;
}



static errno_t myipfilter_output(void *cookie, mbuf_t *data, ipf_pktopts_t options)
{
    if (data)
        log_ip_packet(data, kMyFiltDirOut);
    return myipfilter_output_redirect(cookie, data, options);
    return 0;
}

static errno_t myipfilter_input(void *cookie, mbuf_t *data, int offset, u_int8_t protocol)
{
    if (data)
        log_ip_packet(data, kMyFiltDirIn);
    return myipfilter_input_redirect(cookie, data, offset, protocol);
    return 0;
}

static void myipfilter_detach(void *cookie)
{
    /* cookie isn't dynamically allocated, no need to free in this case */
    struct myfilter_stats* stats = (struct myfilter_stats*)cookie;
    printf("UDP_IN %lu UDP OUT: %lu TCP_IN: %lu TCP_OUT: %lu ICMP_IN: %lu ICMP OUT: %lu OTHER_IN: %lu OTHER_OUT: %lu\n",
           stats->udp_packets[kMyFiltDirIn],
           stats->udp_packets[kMyFiltDirOut],
           stats->tcp_packets[kMyFiltDirIn],
           stats->tcp_packets[kMyFiltDirOut],
           stats->icmp_packets[kMyFiltDirIn],
           stats->icmp_packets[kMyFiltDirOut],
           stats->other_packets[kMyFiltDirIn],
           stats->other_packets[kMyFiltDirOut]);
    
    g_filter_detached = TRUE;
}

static struct ipf_filter g_my_ip_filter = { 
    &g_filter_stats,
    "com.osxkernel.MyIPFilter",
    myipfilter_input,
    myipfilter_output,
    myipfilter_detach
};  

kern_return_t MyIPFilter_start (kmod_info_t * ki, void * d) {
    
    int result, result2;
    
    bzero(&g_filter_stats, sizeof(struct myfilter_stats));
    
    result = ipf_addv4(&g_my_ip_filter, &g_filter_ref);
    
    if (result == KERN_SUCCESS)
        g_filter_registered = TRUE;
    
	
	if(g_filter_registered){
		
		//result2 = ipf_addv6(&g_my_ip_filter, &g_filter_ref);
		
		//if (result2 == KERN_SUCCESS)
		//	g_filter_registered2 = TRUE;
				
	}
	
    return result;
}

kern_return_t MyIPFilter_stop (kmod_info_t * ki, void * d) {
    
    if (g_filter_registered)
    {
        ipf_remove(g_filter_ref);
        g_filter_registered = FALSE;

    }
    /* We need to ensure filter is detached before we return */
    if (!g_filter_detached)
        return EAGAIN; // Try unloading again.
    
    return KERN_SUCCESS;
}
```


----------



## SirDice (Feb 2, 2012)

lopez86100 said:
			
		

> I'm writing a network filter driver for (mac OSX) but I think it's similar to free bsd FreeBSD.


It's not.


----------



## lopez86100 (Feb 2, 2012)

Ok but is that possible that I can get a SOCKET id that was used to make connection from localhost then from user mode (from my application) send duplicated handle to the driver and compare them? *Be*cause the purpose to doing this driver is to redirect all traffic which goes on a specific port but not redirect any connections made from my application.


----------



## SirDice (Feb 3, 2012)

OS-X specific questions are better asked here: http://hintsforums.macworld.com/

Although Apple used parts of FreeBSD, OS-X is an entirely different beast.


----------



## Anonymous (Feb 3, 2012)

lopez86100 said:
			
		

> Ok but is that possible that I can get a SOCKET id that was used to make connection from localhost then from user mode (from my application) send duplicated handle to the driver and compare them? *Be*cause the purpose to doing this driver is to redirect all traffic which goes on a specific port but not redirect any connections made from my application.



Programming a user space program for FreeBSD, I would use local (PF_LOCAL) socket connections from my local program, and of course internet (PF_INET/PF_INET6) socket connections from the ROW. The local connection shouldn't go through any IP filter -  The GNU C Library Reference Manual - 16. Sockets. Socket programming in user space is 100 % the same on Mac OS X.

Kernel space programming is indeed 100 % different between FreeBSD and Mac OS X.


----------

