Модуль, реализующий поддержку протокола Ethernet (net/ethernet/eth.c)

Интерфейс Ethernet (net/ethernet/eth.c) представлен 18 функциями, маркированными тегом EXPORT_SYMBOL(…). Ниже представлены результаты анализа поверхности атаки для этих функций с точки зрения зависимости от сетевого трафика, при этом акцент, на данном этапе, сделан на входящем трафике. Подробный анализ для исходящего трафика не проводился, но выводы, которые можно сделать из краткого разбора, также приведены. Представленные функции для удобства описания разбиты на несколько групп.

Пояснение к диаграммам

Красные прямоугольники – это интерфейсные функции, которые могут являться поверхностью атаки.

Темно-синие прямоугольники – это интерфейсные функции, которые не оперируют сетевыми данными и не является поверхностью атаки.

Зеленые ромбы на диаграммах - это элемент некоторой структуры, который является указателем на функцию. Если из ромба выходит стрелка, значит элементу соответствующей структуры присвоено имя функции, на которую указывает стрелка. Если стрелка входит в ромб из функции, значит в этой функции имеется вызов функции по соответствующему указателю. Зеленые прямоугольники – это структуры, переопределяющие структуру обозначенную зеленым ромбом.

Сводная таблица результатов анализа

Имя функцииНазначениеПоверхность атаки
eth_headerСоздает Ethernet заголовокДа
eth_header_parseИзвлекает mac-адрес из пакетаДа
eth_header_parse_protocolИзвлекает тип протокола из заголовка L2Да
eth_header_cacheСохраняет в кэш Ethernet заголовокДа
eth_header_cache_updateОбновляет соответствующую запись в кэшеДа
ether_setupИнициализация сетевого интерфейсаНет
alloc_etherdev_mqsИнициализация сетевого интерфейсаНет
eth_platform_get_mac_addressПолучение mac-адреса интерфейсаНет
nvmem_get_mac_addressПолучение mac-адреса устройстваНет
sysfs_format_macПредставление mac-адреса устройства в заданном форматеНет
eth_prepare_mac_addr_changeПроверяет, занят ли интерфейсНет
eth_commit_mac_addr_changeПерезаписывает адрес интерфейсаНет
eth_mac_addrУстанавливает новое значение адреса интерфейсаНет
eth_validate_addrПроверяет тип Ethernet адресаНет
eth_get_headlenВозвращает суммарный размер всех заголовков пакета (без пользовательских данных). Да
eth_type_transВозвращает тип Ethernet протокола пакетаДа
eth_gro_receiveМеханизм GROДа
eth_gro_completeМеханизм GROДа

Распределение функций, являющихся поверхностью атаки, по направлению трафика

Имя функцииНаправление трафика
eth_type_transВходящий
eth_gro_receiveВходящий
eth_gro_completeВходящий
eth_header_cache_updateВходящий
eth_header_cacheИсходящий
eth_headerВ обе стороны
eth_header_parseВ обе стороны
eth_header_parse_protocolВ обе стороны
eth_get_headlenВ обе стороны

Группа функций 1

В группу входят:

  • eth_header
  • eth_header_parse
  • eth_header_parse_protocol
  • eth_header_cache
  • eth_header_cache_update

Сетевой интерфейс ядра Linux версии 5.10

Сетевой интерфейс в Linux описывается структурой struct net_device (include/linux/netdevice.h), один из элементов этой структуры - структура struct header_ops, состоящая из нескольких указателей на функции.

struct header_ops {
    int (*create) (struct sk_buff *skb, struct net_device *dev,
        unsigned short type, const void *daddr,
        const void *saddr, unsigned int len);
    int (*parse)(const struct sk_buff *skb, unsigned char *haddr);
    int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);
    void (*cache_update)(struct hh_cache *hh,
        const struct net_device *dev,
        const unsigned char *haddr);
    bool (*validate)(const char *ll_header, unsigned int len);
    __be16 (*parse_protocol)(const struct sk_buff *skb);
};

Для ethernet на ее основе определена другая структура eth_header_ops, в которой используются 5 интерфейсных функций (это экземпляр структуры типа header_ops для протокола ethrnet).

const struct header_ops eth_header_ops = {
    .create = eth_header,
    .parse = eth_header_parse,
    .cache = eth_header_cache,
    .cache_update = eth_header_cache_update,
    .parse_protocol = eth_header_parse_protocol,
};

Некоторым драйверам не подходит структура eth_header_ops, тогда они определяют свою, например (drivers/net/ipvlan/ipvlan_main.c).

static const struct header_ops ipvlan_header_ops = {
    .create = ipvlan_hard_header,
    .parse = eth_header_parse,
    .cache = eth_header_cache,
    .cache_update = eth_header_cache_update,
};

В данном примере некоторым элементам struct header_ops присваиваются интерфейсные функции ethernet, для некоторых определяются собственные функции, а какие-то могут остаться пустыми. Также есть вариант вызова интерфейсной функции ethernet через промежуточную функцию (drivers/net/plip/plip.c):

struct header_ops plip_header_ops = {
    .create = plip_hard_header, // plip_hard_header вызывает eth_header
    .cache = plip_hard_header_cache, // plip_hard_header_cache вызывает eth_header_cache
};

Затем рассмотренные структуры заносятся в структуру сетевого интерфейса net_device:

net_device->header_ops = &ipvlan_header_ops;
net_device->header_ops = &plip_header_ops;

Итак, рассматриваемая группа функций вызывается через структуру struct net_device->header_ops. header_ops представляет собой небольшую специализированную группу функций для работы с заголовками пакетов канального уровня, в нашем случае для работы с интерфейсом ethernet.

eth_header

Функция создает заголовок пакета ethernet. Если адрес отправителя (source addr) не задан, используется адрес текущего интерфейса net_device *dev. Заголовок записывается по адресу во временном указателе на зарезервированную память skb->data:

struct ethhdr *eth = skb_push(skb, ETH_HLEN);

skb_push - резервирование памяти определенного размера, при этом сама память для skb выделена заранее (соответствующего размера), а skb_push просто перемещает указатель skb->data на нужный адрес.

Данная функция может являться поверхностью атаки как для входящего, так и для исходящего трафика.

eth_header_parse

Функция извлекает mac-адрес из пакета. Данная функция может являться поверхностью атаки как для входящего, так и для исходящего трафика.

eth_header_parse_protocol

Функция извлекает тип протокола из заголовка L2. Данная функция может являться поверхностью атаки как для входящего, так и для исходящего трафика.

eth_header_cache

Функция сохраняет в кэш заголовок пакета ethernet. Используется для исходящего трафика, может являться поверхностью атаки.

eth_header_cache_update

Функция обновляет соответствующую запись в кэше. Используется для входящего трафика, может являться поверхностью атаки.

Группа функций 2

В группу входят:

  • ether_setup
  • alloc_etherdev_mqs
  • eth_platform_get_mac_address
  • nvmem_get_mac_address
  • sysfs_format_mac

Сетевой интерфейс ядра Linux версии 5.10

ether_setup

Функция ether_setup выполняет простое присваивание:

struct net_device->header_ops = &eth_header_ops;

плюс задает несколько констант для интерфейсов ethernet:

dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;

Данная функция не оперирует сетевыми данными, и не может быть использована для атаки.

alloc_etherdev_mqs

Функция используется при инициализации сетевого интерфейса, не оперирует сетевыми данными и не является поверхностью атаки. Внутри вызывает функцию ether_setup.

Также есть синонимы alloc_etherdev, alloc_etherdev_mq, devm_alloc_etherdev (include/linux/etherdevice.h):

#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
#define devm_alloc_etherdev(dev, sizeof_priv) devm_alloc_etherdev_mqs(dev, sizeof_priv, 1, 1)

devm_alloc_etherdev_mqs вызывает alloc_etherdev_mqs.

eth_platform_get_mac_address

Вспомогательная функция используется для получения mac-адреса интерфейса. Используется в функциях драйверов через функциональный указатель структуры struct pci_driver.probe. Функция не оперирует сетевыми данными и не является поверхностью атаки.

nvmem_get_mac_address

Вспомогательная функция используется для получения mac-адреса устройства через модуль NVMEM. Используется в функциях драйверов через функциональный указатель структуры struct pci_driver.probe. Функция не оперирует сетевыми данными и не является поверхностью атаки.

sysfs_format_mac

Функция состоит из одной строки:

return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);

Функция используется для представления mac-адреса устройства в заданном формате, не оперирует сетевыми данными и не является поверхностью атаки.

Группа функций 3

В группу входят очень простые вспомогательные функции:

  • eth_prepare_mac_addr_change
  • eth_commit_mac_addr_change
  • eth_mac_addr
  • eth_validate_addr

Сетевой интерфейс ядра Linux версии 5.10

eth_prepare_mac_addr_change

Проверяет, занят ли интерфейс:

if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev))
if (!is_valid_ether_addr(addr->sa_data))

Вызываемая вспомогательная функция bool is_valid_ether_addr (include/linux/etherdevice.h) проверяет, что МАС-адрес не нулевой и не multicast или broadcast, в ней нет обращений ни к каким данным, только сравнение переданного значения с несколькими константами:

return !is_multicast_ether_addr(addr) && !is_zero_ether_addr(addr);
eth_commit_mac_addr_change

Записывает адрес интерфейса в память:

memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
eth_mac_addr

Устанавливает новое значение адреса ethernet. В действительности просто вызывает по очереди две предыдущие функции:

eth_prepare_mac_addr_change(dev, p);
eth_commit_mac_addr_change(dev, p);
eth_validate_addr

Проверяет тип адреса ethernet:

if (!is_valid_ether_addr(dev->dev_addr))

Структура struct net_device_ops (также как упомянутая выше header_ops) является элементом структуры net_device (include/linux/netdevice.h) и содержит указатели на функции. В контексте текущего анализа необходимо рассмотреть только те модули, в которых двум указателям этой структуры ndo_validate_add и ndo_set_mac_address присваиваются адреса двух предыдущих функций:

net_device_ops->ndo_validate_addr = eth_validate_addr;
net_device_ops->ndo_set_mac_address = eth_mac_addr;

Это присваивание выполняется в функциях *_setup, *probe, которые используются в функциях драйверов при инициализации сетевого интерфейса. Функции не оперирует сетевыми данными и не являются поверхностью атаки.

Отдельно отметим, что с точки зрения безопасности может представлять интерес функция eth_commit_mac_addr_change (и соответственно eth_mac_addr), которая перезаписывает сам адрес. Хотя сама функция не зависит от сетевых данных, но может использоваться в других видах атак.

Группа функций 4

В группу входят (см. на предыдущей диаграмме):

  • eth_get_headlen
  • eth_type_trans
eth_get_headlen

Функция возвращает суммарный размер всех заголовков (без пользовательских данных). При этом происходит разбор заголовков пакета нескольких уровней. Функция интересна с точки зрения безопасности данных, поскольку перебирает многоуровневую структуру пакета, но используется только в нескольких драйверах, при этом в большинстве из них eth_get_headlen используется для входящего трафика, но в некоторых для исходящего. Для входящего трафика цепочка вызовов приводит к функциям *_poll(), которые используются модулем NAPI.

eth_type_trans

Функция возвращает тип пакета протокола ethernet, а также устанавливает тип пакета: skb->pkt_type = PACKET_BROADCAST | PACKET_MULTICAST | PACKET_OTHERHOST. Функция обращается к сетевым данным и интересна с точки зрения безопасности данных. Проведенный не полный (в силу большого объема работы) анализ кода показывает, что данная функция используется только для входящего трафика.

Группа функций 5

В группу входят (см. диаграмму в разделе "Группа функций 2"):

  • eth_gro_receive
  • eth_gro_complete

В Linux существует механизм GRO (Generic Receive Offload), выполняющий объединение входящих пакетов по определенным критериям на уровне сетевых драйверов так, что сетевой стек операционной системы видит их гораздо меньше. Для использования данного механизма каждый протокол, который использует GRO, определяет соответствующие функции gro_receive и gro_complete. Функция gro_receive используется для накопления входящих пакетов, удовлетворяющих определенным критериям. Функция gro_complete выполняет объединение накопленных пакетов в один и отправку этого пакета сетевому стеку для дальнейшей обработки. Две выше представленные функции используются для специального вида трафика: инкапсуляции фреймов ethernet в пакеты UDP или GRE.

Заключение

Проведенный анализ модуля Ethernet (net/ethernet/eth.c) показал, что из 18 функций этого модуля 9 не зависят от сетевых данных, 9 представляют поверхность атаки для сетевого трафика. Из последних 9 функций: 1 используется для исходящего трафика, 4 – для входящего, 4 – могут использоваться в обоих направлениях. Обобщенные данные представлены выше в таблицах.