Как изменить sk_buff и переслать снова из linux ядра - PullRequest
0 голосов
/ 12 февраля 2020

Я пытаюсь создать простой модуль ядра, используя ловушку netfilter, которая изменит адрес источника любого пакета и передаст снова, как NAT. Новый адрес источника будет сохранен в массиве s_entries в программе ниже.

Но я не имею ни малейшего представления о том, какие все связанные параметры я должен изменить как часть этого, и как его отправить. вне. Пожалуйста, найдите следующий код, где я поместил 'TODO' в этой программе. Может ли кто-нибудь помочь мне найти решение для этого? Я видел некоторую нить, связанную NF_HOOK, чтобы отправить снова. Но может кто-нибудь сказать мне, как использовать NF_HOOK для отправки пакета с PRE_ROUTING_HOOK?

#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/module.h>
#include <linux/kernel.h>    // included for KERN_INFO
#include <linux/init.h>      // included for __init and __exit macros
#include <net/ip.h>
#include "defines.h"
#include <linux/inetdevice.h>

#define NIPQUAD(addr)  ((unsigned char *)&addr)[0], ((unsigned char *)&addr)[1], ((unsigned char *)&addr)[2], ((unsigned char *)&addr)[3]

static sock_data_t **s_entries = NULL;
static int         set_index   = 0;
static int         get_index   = 0;


static sock_data_t *get_new_src_entry(uint ip, ushort port) {
    int i;

    for(i = 0; i < MAX_NUM_ENTRIES; i++) {
        if(s_entries[i] && s_entries[i]->baddr.ipaddr == ip &&  s_entries[i]->baddr.port == port) {
            return s_entries[i];
        }
    }
    return NULL;
}


static sock_data_t *get_matching_dest(uint ip, ushort port) {
    int i;

    for(i = 0; i < MAX_NUM_ENTRIES; i++) {
        if(s_entries[i] && s_entries[i]->xaddr.ipaddr == ip && s_entries[i]->xaddr.port == port) {
            return s_entries[i];
        }
    }
    return NULL;
}

static unsigned int ip_simple_nat_hook_v4(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
    struct iphdr *iph = ip_hdr(skb);
    struct udphdr *udph;
    struct tcphdr *tcph;
    ushort dst_port = 0;
    ushort src_port = 0;
    sock_data_t *entry = NULL;
    // struct net_device *dev;

    if(iph->protocol == IPPROTO_UDP) {
        udph = udp_hdr(skb);
        dst_port = ntohs(udph->dest);
        src_port = ntohs(udph->source);
    } else if(iph->protocol == IPPROTO_TCP) {
        tcph = tcp_hdr(skb);
        dst_port = ntohs(tcph->dest);
        src_port = ntohs(tcph->source);
    }

    if(dst_port != 1250) return NF_ACCEPT;

    if(skb->dev)
        printk(KERN_INFO "simple_nat: Hook: %u; Interface name:%s\n", state->hook, skb->dev->name);
    else
        printk(KERN_INFO "simple_nat: No destination in sk_buff[%d]\n", state->hook);

    entry = get_new_src_entry(iph->saddr, src_port);
    if(entry) {
        // TODO: modify iph->saddr with entry entry->baddr.ipaddr and send again.
        return NF_ACCEPT;
    }

    printk(KERN_INFO "%pI4:%d -- %pI4:%d\n", &iph->saddr, src_port, &iph->daddr, dst_port);
    return NF_ACCEPT;
}


static struct nf_hook_ops ip_simple_nat_pre_v4 = {
    .hook = (nf_hookfn*)ip_simple_nat_hook_v4,
    .pf = PF_INET,
    .hooknum = NF_INET_PRE_ROUTING,
    .priority = NF_IP_PRI_FIRST
};

/**
 * @brief: Set set index to next empty space
 * @return: 0 successful
 *          -1 loop completed and no space left
 */
static unsigned short set_next_set_index(void) {
    int tmp_idx = set_index;
    while(1) {
        ++set_index;
        if(set_index == MAX_NUM_ENTRIES)
            set_index = 0;

        if(set_index == tmp_idx) {
            return -1;
        }

        if(!s_entries[set_index]) {
            return 0;
        }
    }
}

/**
 * @brief: Set get index to next non-empty space
 * @return: 0 successful
 *          -1 loop completed and no item found
 */
static unsigned short set_next_get_index(void) {
    int tmp_idx = get_index;
    while(1) {
        ++get_index;
        if(get_index == MAX_NUM_ENTRIES)
            get_index = 0;

        if(get_index == tmp_idx) {
            return -1;
        }

        if(s_entries[get_index]) {
            return 0;
        }
    }
}

/**
 * @brief: setsockopt controls
 */
static int simple_nat_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
{
    sock_data_t *data;

    switch(cmd) {
        case SN_SET_ENTRY:
            if(set_next_set_index()) {
                printk(KERN_INFO "simple_nat: No free space\n");
                return -EFAULT;
            }

            // If size of data is not as expected; discard it.
            if(len != sizeof(sock_data_t)) {
                printk(KERN_INFO "simple_nat: Invalid data given\n");
                return -EFAULT;
            }

            // Allocate memory in the array.
            s_entries[set_index] = (sock_data_t *)kzalloc(sizeof(sock_data_t), GFP_ATOMIC);
            if(!s_entries[set_index]) {
                return -ENOMEM;
            }

            // Copy data to kernel memeory
            if(copy_from_user(s_entries[set_index], user, sizeof(sock_data_t))) {
                return -EFAULT;
            }
            data = s_entries[set_index];

            printk(KERN_INFO "simple_nat: New entry crated at %d\n", set_index);
            break;

        case SN_REST_IDX:
            get_index = 0;
            break;
    }
    return 0;
}

/**
 * @brief: getsockopt controls
 */
static int simple_nat_get_ctl(struct sock *sk, int cmd, void *user, int *len)
{
    switch(cmd) {
        case SN_GET_ENTRY:
            if(set_next_get_index()) {
                printk(KERN_INFO "simple_nat: Loop completed\n");
                return -EFAULT;
            }

            if(!s_entries[get_index] || copy_to_user(user, s_entries[get_index], *len)) {
                return -EFAULT;
            }
            break;
    }
    return 0;
}

static struct nf_sockopt_ops simple_nat_sockopt = {
    .pf         = PF_INET,
    .set_optmin = SN_SO_BASE_CTL,
    .set_optmax = SN_SO_MAX_CTL,
    .set        = simple_nat_set_ctl,
    .get_optmin = SN_SO_BASE_CTL,
    .get_optmax = SN_SO_MAX_CTL,
    .get        = simple_nat_get_ctl,
};


static int __init simple_nat_init(void) {
    int ret = 0;
    printk(KERN_INFO "simple_nat: Module inserted\n");
    s_entries = kzalloc(sizeof(sock_data_t *) * MAX_NUM_ENTRIES, GFP_KERNEL);
    if(!s_entries) {
        printk("simple_nat: Memory allocation failed\n");
        return -1;
    }

    ret = nf_register_net_hook(&init_net, &ip_simple_nat_pre_v4);
    if(ret < 0) {
        printk(KERN_ERR "simple_nat: Pre-Hook Failed\n");
        goto pre_hook_exit;
    }

    ret = nf_register_sockopt(&simple_nat_sockopt);
    if(ret < 0) {
        printk(KERN_ERR "simple_nat: setting sockopt failed Failed\n");
        goto sock_opt_exit;
    }

    return 0;

sock_opt_exit:
    nf_unregister_net_hook(&init_net, &ip_simple_nat_pre_v4);

pre_hook_exit:
    kfree(s_entries);

    return -1;
}


static void free_entry(int idx) {
    if(s_entries[idx]) {
        kfree(s_entries[idx]);
        s_entries[idx] = NULL;
    }
}

static void free_all_entries(void) {
    int idx;

    for(idx = 0; idx < MAX_NUM_ENTRIES; idx++) {
        free_entry(idx);
    }
}

static void __exit simple_nat_exit(void) {
    nf_unregister_net_hook(&init_net, &ip_simple_nat_pre_v4);
    nf_unregister_sockopt(&simple_nat_sockopt);

    free_all_entries();

    if(s_entries) {
        kfree(s_entries);
    }

    printk(KERN_INFO "simple_nat: Module exit\n");
}


module_init(simple_nat_init);
module_exit(simple_nat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shafeeque");
MODULE_DESCRIPTION("A Simple NAT module");

Спасибо, Shafeeque

...