LINUX.ORG.RU

Не вызывается rx_handler в модуле ядра

 ,


0

1

Добрый день, прошу помощи. Написал простейший драйвер с функцией перехвата пакетов на интерфейсе(drop_packet). Зарегистрировал ее с помощью netdev_rx_handler_register. И для пакетов, пришедших извне(при посылке с ноутбука на интерфейс) drop_packet дергается исправно, но при посылке пакета, сформированного локально(пробовал scapy и ping) drop_packet не вызывается.


#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <net/arp.h>

#define MODULE_NAME "drop_packets"
#define VERSION "1.0.0"
#define PFX MODULE_NAME": "
#define TARGET_DEV "enp0s8"
#define DBG_PRINT(fmt, args...) \
    do \
    { \
        printk(KERN_ERR PFX "%s(%d)-%s:\n"fmt"\n", __FILE__,__LINE__,__FUNCTION__,##args); \
    } while (0)

typedef struct {
    struct net_device *ndev;
    int promisc_old, promisc_count;
    int old_netpoll;

} netif_info;

static netif_info pt_netif;

#define netif_info_get_rcu(dev) \
    (rcu_dereference(dev->rx_handler_data))

static rx_handler_result_t drop_packet(struct sk_buff **pskb)
{
    struct sk_buff *const skb = *pskb;
    DBG_PRINT("LALALA %u", skb->len);
    kfree_skb(*pskb);
    *pskb = NULL;
    return RX_HANDLER_CONSUMED;
}

static int __init the_init(void)
{
    int err = 0;
    struct net *const net = current->nsproxy->net_ns;
    struct net_device *const ndev = dev_get_by_name(net, TARGET_DEV);
    DBG_PRINT("dev_name=%s %s %i", TARGET_DEV, NULL != ndev ? "OK" : "FAIL", ndev->addr_len);

    pt_netif.ndev = ndev;
    pt_netif.promisc_old = ndev->flags & IFF_PROMISC;
    pt_netif.promisc_count = 0;
    pt_netif.old_netpoll = ndev->priv_flags & IFF_DISABLE_NETPOLL;
    DBG_PRINT("priv_flags is %hx %hx", ndev->priv_flags, ndev->flags);

    rtnl_lock();
    if(!pt_netif.promisc_old) {
        err = dev_set_promiscuity(ndev, 1);
        DBG_PRINT("dev_set_promiscuity(1) %i", err);
        DBG_PRINT("priv_flags is %x %x", ndev->priv_flags, ndev->flags);
    }
    if(!pt_netif.old_netpoll) {
        ndev->priv_flags |= IFF_DISABLE_NETPOLL;
    }

    //ndev->priv_flags |= IFF_DISABLE_NETPOLL;
    //ndev->flags |= IFF_PROMISC;
    err = netdev_rx_handler_register(ndev, drop_packet, &pt_netif);
    rtnl_unlock();
    return err;
}

static void __exit the_exit(void)
{
    int err;
    struct net_device *const ndev = pt_netif.ndev;
    rtnl_lock();
    if(!pt_netif.promisc_old) {
        err = dev_set_promiscuity(ndev, -1);
        DBG_PRINT("dev_set_promiscuity(-1) %i", err);
        DBG_PRINT("priv_flags is %x %x", ndev->priv_flags, ndev->flags);
    }
    if(!pt_netif.old_netpoll) {
        ndev->priv_flags &= ~(IFF_DISABLE_NETPOLL);
    }
    netdev_rx_handler_unregister(ndev);
    rtnl_unlock();

    dev_put(ndev);
}

module_init(the_init);
module_exit(the_exit);

MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Serg");
MODULE_DESCRIPTION("drop all packets received from specified net device");

Ответ на: комментарий от Axrud

но при посылке пакета, сформированного локально(пробовал scapy и ping) drop_packet не вызывается.

Это единственная проблема которую ты описал.

Если ты имел ввиду отправку локально сформированного пакета на адрес интерфейса, то ты не увидишь его т.к. он будет передан (и принят) через lo интерфейс согласно таблице маршрутизации.

vel ★★★★★
()
Ответ на: комментарий от vel

drop_packet - это хендлер, который должен вызываться для входящих на интерфейс пакетов, по прежнему не понимаю, причем здесь исходящий трафик.

Если ты имел ввиду отправку локально сформированного пакета

Да, я это имел ввиду. Я ведь об этом и спросил:

но при посылке пакета, сформированного локально(пробовал scapy и ping) drop_packet не вызывается

то ты не увидишь его т.к. он будет передан (и принят) через lo интерфейс согласно таблице маршрутизации.

однако его прекрасно видит tcpdump, работающий через pcap(который в свою очередь работает через netfilter). Т.е. в ядре информация об этом пакете вполне себе присутствует.

Axrud
() автор топика
Ответ на: комментарий от Axrud

drop_packet - это хендлер, который должен вызываться для входящих на интерфейс пакетов, по прежнему не понимаю, причем здесь исходящий трафик.

пакеты которые ты генеришь локально с локальным адресом назначения не попадают в твой интерфейс, они попадают в dev lo.

однако его прекрасно видит tcpdump, работающий через pcap(который в свою очередь работает через netfilter)

гм. tcpdump будет работать через нетфильтер только если ты используешь «tcpdump -i nflog:X» или «tcpdump -i nfqueue:X», а обычном варианте он работает через af_packet или raw-сокет.

Ты хочешь сказать, что запустив «tcpdump -ni ethX» видишь пакеты посылаемые через запущеный локально ping на адрес интерфейса ethX ? В нормальной системе такого не должно происходить.

Запусти «tcpdump -ni any icmp » и «ping xx.xx.xx.xx», где xx.xx.xx.xx это адрес твоего сетевого интерфейса. Ты увидишь через какой интерфейс этот пакет идет.

Посмотри на картинке https://inai.de/images/nf-packet-flow.svg где af_packet.

А netdev_rx_handler_register подключает обработчик существенно раньше - в net_rx_action. См. схему в конце https://serverfault.com/questions/784886/policy-routing-for-local-outcoming-c...

vel ★★★★★
()
Ответ на: комментарий от Axrud

Т.е. в ядре информация об этом пакете вполне себе присутствует.

Присутствует, но для локальных коммуникаций пакеты гоняются через lo.

Допустим, ядро продавит пакет в драйвер физического сетевого адаптера. Тот положит пакеты в очередь отправки, они уйдут к пиру. Например, к маршрутизатору. Дальше что? Предполагается, что маршрутизатор должен отправить пакеты обратно, чтобы физический сетевой адаптер их принял, а ядро уже в итоге продавило в локально запущенный пользовательский процесс? Звучит как бессмысленное ухудшение. Проще и эффективнее всё прогонять через lo.

i-rinat ★★★★★
()
Ответ на: комментарий от vel

Ты хочешь сказать, что запустив «tcpdump -ni ethX» видишь пакеты посылаемые через запущеный локально ping на адрес интерфейса ethX ? В нормальной системе такого не должно происходить

конечно вижу. А в чем проблема? Вот один из моих интерфейсов, я его вообще не настраивал и на нем нет сетевого адреса:

3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:56:7e:74 brd ff:ff:ff:ff:ff:ff

Запускаем tcpdump:

tcpdump -vvXX -i enp0s8

Пишем простейший скрипт на scapy(маки произвольные), чтобы послать пакет на интерфейс:

#!/bin/python
from scapy.all import *

pkt24=Ether(src="00:11:22:33:44:55", dst="00:80:c2:00:00:00")/IP()
sendp(pkt24, iface="enp0s8")

Запускаем его и видим выхлоп тспдампа:

tcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
10:27:41.126951 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto Options (0), length 20)
    localhost > localhost:  hopopt 0
	0x0000:  0080 c200 0000 0011 2233 4455 0800 4500
	0x0010:  0014 0001 0000 4000 7ce7 7f00 0001 7f00
	0x0020:  0001

но если на этот интерфейс(в данном случае enp0s8) повесить хендлер при помощи netdev_rx_handler_register, то он не дергается почему-то.

Насчет ping - это будет не очень чистый эксперимент, послав что-то на адрес интерфейса, утилита действительно может решить, что адрес локальный и пошлет его на lo вместо нужного нам интерфейса.

Axrud
() автор топика
Ответ на: комментарий от Axrud

Запускаем его и видим выхлоп тспдампа:

Ну так ясен пень! В схеме https://inai.de/images/nf-packet-flow.svg после egress(qdisc) есть af_packet, значит tcpdump его покажет.

но если на этот интерфейс(в данном случае enp0s8) повесить хендлер при помощи netdev_rx_handler_register, то он не дергается почему-то.

А схрена ли он должен срабатывать? Кто тебе его назад вернул?

То, что ты promisc-mode включил на интерфейсе, это не значит, что все отправленные пакеты будут видны на входе сетевухи.

У тебя с другой стороны коммутатор и если там обычный порт, то он тебе пакет не вернёт. Возможно, если ты сначала анонсируешь на своём порту мас-адрес на который ты посылаешь пакет, то коммутатор тебе вернёт его, но я сильно сомневаюсь в этом.

vel ★★★★★
()
Ответ на: комментарий от vel

Я ничего не понял из этого объяснения.

Кто тебе его назад вернул?

Зачем его куда-то возвращать, если этот пакет есть на интерфейсе?

У тебя с другой стороны коммутатор

С какой другой стороны? я локально посылаю пакет на интерфейс, к которому ничего не подключено.

tcpdump … работает через af_packet или raw-сокет. А netdev_rx_handler_register подключает обработчик существенно раньше - в net_rx_action

Если netdev_rx_handler_register подключает обработчик существенно раньше, то почему в нем пакет не ловится, а ловится через сокет?

Axrud
() автор топика
Ответ на: комментарий от Axrud

Если netdev_rx_handler_register подключает обработчик существенно раньше, то почему в нем пакет не ловится, а ловится через сокет?

Исходящий пакет не является входящим пакетом. netdev_rx_handler получает только входящие.

tcpdump видит и входящие и исходящие пакеты.

vel ★★★★★
()