Модуль, реализующий программный многоуровневый коммутатор Open vSwitch (net/openvswitch)

Введение

В данной работе представлены результаты анализа поверхности атаки для модуля openvswitch с точки зрения зависимости от сетевого трафика.

Модуль Open vSwitch обеспечивает через пользовательское пространство гибкое управление обработкой пакетов на основе потоков на сетевых устройствах. Модуль может использоваться для реализации простого коммутатора Ethernet, соединения сетевых устройств, обработки VLAN, управления доступом к сети, управления сетью на основе потоков и др.

Open vSwitch реализует множество "datapaths" ("путь передачи данных", аналог моста / bridge), каждый из которых может иметь несколько виртуальных портов "vports" (аналог портов внутри моста). К каждому пути (datapath) привязана собственная "таблица потоков" (flow table), которую заполняет пользователь (userspace). Каждый поток (flow) сопоставляет ключи (на основе заголовков пакетов и метаданных) с наборами действий (actions). В модуле реализовано множество действий (actions), наиболее распространенное - перенаправляет пакет на другой vport.

Когда пакет поступает на vport, Open vSwitch обрабатывает его, извлекая из него ключ потока (flow key), по которому происходит поиск в таблице потоков. Если соответствующий поток найден, то выполняется соответствующий набор действий. Если поток не найден, пакет передается в пользовательское пространство (помещается в соответствующую очередь) для дальнейшей обработки (чаще всего, пользователь в ответ создает новый поток для обработки подобных пакетов полностью в ядре).

Совместимость с ключами потока

Поскольку сетевые протоколы эволюционируют со временем, в новых версиях модуля Open vSwitch может быть добавлена поддержка новых протоколов и удалена поддержка устаревших. Соответственно могут изменятся извлекаемые из пакетов ключи потоков. Для этих целей интерфейс Netlink модуля разработан таким образом, чтобы позволить приложениям пользовательского пространства работать с любой версией ключа потока. Для целей совместимости версий, когда модуль ядра передает пакет в пользовательское пространство, он также передает ключ потока, который извлечен из пакета. Затем пользовательское приложение извлекает из пакета свою версию ключа и сравнивает его с ключем ядра:

  • Если ключи совпадают, никаких действий не требуется.
  • Если ключ ядра содержит больше полей, чем версия ключа пользователя (например, ядро декодировало заголовок IPv6, а приложение остановилось на заголовке Ethernet, поскольку IPv6 не понимает), никаких действий также не требуется. Приложение по-прежнему может настроить поток обычным способом, если используется ключ, предоставленный ядром.
  • Если ключ пользователя содержит больше полей, чем у ядра, тогда пользовательское приложение может переслать пакет самостоятельно, не настраивая поток в ядре. Этот вариант ухудшает производительность, поскольку каждый пакет потока, должен отправляться в пользовательское пространство, но поведение пересылки будет корректным. Также приложение может определить, что значения дополнительных полей ключа не влияют на поведение пересылки. В этом случае, оно по-прежнему может настроить поток в ядре.

Open vSwitch позволяет использовать интерфейс Netlink в четырех вариантах:

  • Транзакции. Транзакция аналогична системному вызову, ioctl или RPC: пользовательское приложение отправляет запрос ядру и сразу получает от него ответ или сообщение об ошибке.
  • Дампы. Запрос типа Дамп запрашивает у ядра всю информацию из таблицы. Ответ ядра состоит из произвольного количества сообщений. Ответ обычно генерируется и передается по частям, а не целиком, и может представлять собой несогласованный снимок таблицы в случаях, когда содержимое таблицы изменялось во время дампа.
  • Подписка на многоадресные рассылки (Multicast subscriptions). Большинство реализаций ядра Netlink позволяют отслеживать изменения в таблице, подключившись к группе многоадресной рассылки для этой таблицы. Всякий раз, когда содержимое таблицы изменяется, модуль Netlink отправляет сообщение всем подписавшимся сокетам.
  • Подписка на одноадресную рассылку (Unicast subscriptions). Пользователь может присвоить виртуальному порту "vport" один или несколько идентификаторов Netlink (Netlink PID) в качестве "upcall PIDs". Когда пакет, пришедший на такой vport, не соответствует ни одному потоку в таблице соответствующего пути "datapath", ядро хэширует некоторые заголовки пакета, использует этот хэш для выбора одного из присвоенных PID и отправляет пакет (инкапсулированный в сообщение Open vSwitch Netlink) в сокет с выбранным PID. Основная причина использования нескольких PID для одного vport - затруднить высокоскоростным оправителям заглушение трафика оправителей с низкой производительностью.
Основная функциональность модуля:

actions.c
conntrack.c – поддержка "Connection tracking"
datapath.c – инициализация модуля и управление объектами datapath (аналог моста/ bridge)
datapath.h
dp_notify.c
meter.c – сбор статистики

Управление потоками:

flow.c
flow.h
flow_netlink.c – поддержка протокола netlink
flow_table.c – управление таблицами потоков

Управление виртуальными портами:

vport.c
vport-geneve.c – поддержка Geneve vport
vport-gre.c – поддержка GRE vport
vport-internal_dev.c
vport-netdev.c
vport-vxlan.c – поддержка VXLAN vport

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

Косые четырехугольники – это статические (static) функции, которые вызываются или через функциональный указатель или внутри модуля.
Красным цветом выделены интерфейсные функции, которые могут являться поверхностью атаки.
Оранжевым цветом выделены интерфейсные функции, которые могут являться поверхностью атаки при вызовах из недоверенного пользовательского пространства.
Розовым цветом выделены интерфейсные функции, требующие более глубокого анализа с точки зрения поверхности атаки.
Темно-серые прямоугольники – это интерфейсные функции, которые не оперируют сетевыми данными и не является поверхностью атаки.
Зеленые ромбы на диаграммах – это один или несколько элементов некоторой структуры, которые являются указателями на функции. Если из ромба выходит стрелка, значит элементу соответствующей структуры присвоено имя функции, на которую указывает стрелка. Если стрелка входит в ромб из функции, значит в этой функции имеется вызов функции по соответствующему указателю. Зеленые прямоугольники: если они относятся к ромбам, то это структуры, переопределяющие структуру обозначенную зеленым ромбом; самостоятельные прямоугольники – это статические функциональные указатели.
Желтые квадраты с цифрой – имеется дубликат функции на диаграмме.
Пунктирная стрелка – это использование функциональных указателей.
Стрелка с ромбом – вызов функции через промежуточные (неуказанные) функции.

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

openvswitch

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

Имя подмодуля и/или функцииНазначениеПоверхность атаки
actions.c:
ovs_dst_get_mtu action_fifos_init action_fifos_exitНет
ovs_execute_actionsДа
conntrack.c:
ovs_ct_verify
ovs_ct_copy_action ovs_ct_action_to_attr
ovs_ct_free_action
ovs_ct_init
ovs_ct_exit
Нет
ovs_ct_fill_key
ovs_ct_put_key
ovs_ct_execute
ovs_ct_clear
Да
ovs_ct_limit_cmd_set
ovs_ct_limit_cmd_del
ovs_ct_limit_cmd_get
Да
datapath.c:
ovs_packet_cmd_execute
ovs_flow_cmd_new
ovs_flow_cmd_set
ovs_flow_cmd_get
ovs_flow_cmd_del
ovs_flow_cmd_dump
ovs_dp_cmd_new
ovs_dp_cmd_del
ovs_dp_cmd_set
ovs_dp_cmd_get
ovs_dp_cmd_dump
ovs_vport_cmd_new
ovs_vport_cmd_set
ovs_vport_cmd_del
ovs_vport_cmd_get
ovs_vport_cmd_dump
Да
ovs_dp_process_packet
ovs_dp_upcall
Да
ovs_lock
ovs_unlock
lockdep_ovsl_is_held
ovs_dp_name
ovs_lookup_vport
ovs_dp_detach_port
ovs_vport_cmd_build_info
ovs_dp_masks_rebalance
Нет
datapath.hНет
dp_notify.cНет
flow.c:
ovs_flow_used_time
ovs_flow_stats_get
ovs_flow_stats_clear
Нет
ovs_flow_stats_update
ovs_flow_key_update_l3l4
ovs_flow_key_update
ovs_flow_key_extract
ovs_flow_key_extract_userspace
Да
flow.h:
sw_flow_key_is_nd
ovs_mac_header_len
Да
ovs_key_mac_proto
__ovs_mac_header_len
ovs_identifier_is_ufid
ovs_identifier_is_key
Нет
flow_netlink.c:
parse_flow_nlattrs
ovs_nla_put_tunnel_info
nsh_hdr_from_nlattr
nsh_key_from_nlattr
ovs_nla_get_match
ovs_nla_get_ufid
ovs_nla_get_identifier
ovs_nla_get_ufid_flags
ovs_nla_get_flow_metadata
ovs_nla_put_identifier
ovs_nla_put_masked_key
ovs_nla_put_mask
ovs_nla_add_action
ovs_match_init
ovs_nla_copy_actions
ovs_nla_put_actions
Да
ovs_tun_key_attr_size
ovs_key_attr_size
ovs_nla_free_flow_actions
ovs_nla_free_flow_actions_rcu
Нет
ovs_nla_put_keyДа
flow_table.c:
ovs_flow_alloc
ovs_flow_tbl_count
ovs_flow_free
ovs_flow_tbl_masks_cache_resize
ovs_flow_tbl_init
table_instance_flow_flush
ovs_flow_tbl_destroy
ovs_flow_tbl_dump_next
ovs_flow_tbl_flush
ovs_flow_tbl_lookup
ovs_flow_tbl_lookup_exact
ovs_flow_cmp
ovs_flow_tbl_lookup_ufid
ovs_flow_tbl_num_masks
ovs_flow_tbl_masks_cache_size
ovs_flow_tbl_remove
ovs_flow_tbl_insert
ovs_flow_masks_rebalance
ovs_flow_init
ovs_flow_exit
Нет
ovs_flow_mask_key
ovs_flow_tbl_lookup_stats
Да
meter.c:
ovs_meter_cmd_features
ovs_meter_cmd_set
ovs_meter_cmd_get
ovs_meter_cmd_del
Да
ovs_meter_execute
ovs_meters_init
ovs_meters_exit
Нет
vport.c:
__ovs_vport_ops_register
ovs_vport_ops_unregister
ovs_vport_alloc
ovs_vport_free
ovs_vport_init
ovs_vport_exit
ovs_vport_locate
ovs_vport_add
ovs_vport_del
ovs_vport_get_stats
ovs_vport_get_options
ovs_vport_get_upcall_portids
Нет
ovs_vport_set_options
ovs_vport_set_upcall_portids
ovs_vport_find_upcall_portid
ovs_vport_receive
ovs_vport_send
vport-geneve.cНет
vport-gre.cНет
vport-internal_dev.c:
internal_dev_open
internal_dev_stop
internal_get_stats
internal_dev_getinfo
internal_dev_create
internal_dev_destroy
ovs_is_internal_dev
ovs_internal_dev_get_vport
ovs_internal_dev_rtnl_link_unregister
Нет
internal_dev_xmit internal_dev_recv
ovs_internal_dev_rtnl_link_register
Да
vport-netdev.c:
ovs_netdev_link
ovs_netdev_tunnel_destroy
ovs_netdev_detach_dev
ovs_netdev_get_vport
ovs_netdev_init
ovs_netdev_exit
netdev_create
netdev_destroy
Нет
netdev_frame_hookДа
vport-vxlan.cНет

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

Имя функцииНаправление трафика
actions.c
ovs_execute_actionsВ обе стороны
conntrack.c
ovs_ct_executeВходящий
ovs_ct_fill_key
ovs_ct_put_key
ovs_ct_clear
В обе стороны
ovs_ct_limit_cmd_set
ovs_ct_limit_cmd_del
ovs_ct_limit_cmd_get
Из пользовательского пространства
datapath.c
ovs_packet_cmd_execute
ovs_flow_cmd_new
ovs_flow_cmd_set
ovs_flow_cmd_get
ovs_flow_cmd_del
ovs_flow_cmd_dump
ovs_dp_cmd_new
ovs_dp_cmd_del
ovs_dp_cmd_set
ovs_dp_cmd_get
ovs_dp_cmd_dump
ovs_vport_cmd_new
ovs_vport_cmd_set
ovs_vport_cmd_del
ovs_vport_cmd_get
ovs_vport_cmd_dump
Из пользовательского пространства
ovs_dp_process_packet
ovs_dp_upcall
В обе стороны
flow.c
ovs_flow_key_update_l3l4
ovs_flow_key_extract
Входящий
ovs_flow_stats_update
ovs_flow_key_update
В обе стороны
ovs_flow_key_extract_userspaceИсходящий
flow.h
ovs_mac_header_lenИсходящий
sw_flow_key_is_ndВ обе стороны
flow_netlink.c
parse_flow_nlattrs
ovs_nla_put_tunnel_info
nsh_hdr_from_nlattr
nsh_key_from_nlattr
ovs_nla_get_match
ovs_nla_get_ufid
ovs_nla_get_identifier
ovs_nla_get_ufid_flags
ovs_nla_get_flow_metadata
ovs_nla_put_identifier
ovs_nla_put_masked_key
ovs_nla_put_mask
ovs_nla_add_action
ovs_match_init
ovs_nla_copy_actions
ovs_nla_put_actions
Из пользовательского пространства
ovs_nla_put_keyВ обе стороны
flow_table.c
ovs_flow_mask_key
ovs_flow_tbl_lookup_stats
В обе стороны
meter.c
ovs_meter_cmd_features
ovs_meter_cmd_set
ovs_meter_cmd_get
ovs_meter_cmd_del
Из пользовательского пространства
vport.c
ovs_vport_set_options
ovs_vport_set_upcall_portids
Из пользовательского пространства
ovs_vport_find_upcall_portidВ обе стороны
ovs_vport_receiveВходящий
ovs_vport_sendИсходящий
vport-internal_dev.c
internal_dev_xmit
internal_dev_recv
Исходящий
vport-netdev.c
netdev_frame_hookВходящий
Замечания:

Структуры, обозначенные зелеными ромбами, не изучались детально на предмет вызовов из других функций. Часто к ним есть доступ из пользовательского пространства, поэтому такие структуры могут рассматриваться как поверхность атаки 3 уровня, но большинство таких операций, как правило, требует права администратора.

Данный модуль использует некоторые поля пакета для создания ключа потока (см. Введение) через структуру struct sw_flow_key. Поэтому функции, принимающие sw_flow_key в качестве аргумента, могут рассмариватся в качестве поверхности атаки.

Поскольку коммутаторы занимаются перераспределением трафика, классификация пакетов по типу Входящий/Исходящий не точная. Большинство исходящих пакетов для некоторого порта vport являются входящими для другого порта (т.е. фактически это входящий трафик). С другой стороны, пользователь через интерфейс Netlink имеет возможность самостоятельно перенаправлять пакеты (см. Введение), а значит, может создавать собственный исходящий трафик.

Ниже перечислены все файлы и функции, доступные для вызова вне модуля.

actions.c
static unsigned int ovs_dst_get_mtu(const struct dst_entry *dst)

static struct dst_ops ovs_dst_ops = {
	.family = AF_UNSPEC,
	.mtu = ovs_dst_get_mtu,
};

int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_actions *acts,
			struct sw_flow_key *key)
int action_fifos_init(void)
void action_fifos_exit(void)

С сетевыми данными работает функция ovs_execute_actions, которая используется для выполнения различных действий с пакетом, является поверхностью атаки для обоих направлений трафика.

conntrack.c

Поддержка "Connection tracking":

#if IS_ENABLED(CONFIG_NF_CONNTRACK)
void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
int ovs_ct_put_key(const struct sw_flow_key *swkey,
		   const struct sw_flow_key *output, struct sk_buff *skb)
int ovs_ct_execute(struct net *net, struct sk_buff *skb, struct sw_flow_key *key,
		   const struct ovs_conntrack_info *info)
int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key)
bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr)
int ovs_ct_copy_action(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key,
		       struct sw_flow_actions **sfa,  bool log)
int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, struct sk_buff *skb)
void ovs_ct_free_action(const struct nlattr *a)
int ovs_ct_init(struct net *net)
void ovs_ct_exit(struct net *net)
#else
// функции ничего не делают (отсутствует код или возвращают константное значение)

static int ovs_ct_limit_cmd_set(struct sk_buff *skb, struct genl_info *info)
static int ovs_ct_limit_cmd_del(struct sk_buff *skb, struct genl_info *info)
static int ovs_ct_limit_cmd_get(struct sk_buff *skb, struct genl_info *info)

static const struct genl_small_ops ct_limit_genl_ops[] = {
	{ .cmd = OVS_CT_LIMIT_CMD_SET,
		.doit = ovs_ct_limit_cmd_set,
	},
	{ .cmd = OVS_CT_LIMIT_CMD_DEL,
		.doit = ovs_ct_limit_cmd_del,
	},
	{ .cmd = OVS_CT_LIMIT_CMD_GET,
		.doit = ovs_ct_limit_cmd_get,
	},
};
 
struct genl_family dp_ct_limit_genl_family __ro_after_init = {
	.name = OVS_CT_LIMIT_FAMILY,
	.small_ops = ct_limit_genl_ops,
};

Функциональные указатели структуры struct genl_small_ops доступны через интерфейс Generic Netlink (net/netlink/genetlink.c) и могут являются поверхностью атаки при вызовах из пользовательского пространства.

Функция ovs_ct_execute работает с входящим трафиком (внутри используется функция handle_fragments для дефрагментации пакетов), функции ovs_ct_fill_key, ovs_ct_put_key и ovs_ct_clear используются в обоих направлениях.

datapath.c

Инициализация модуля и управление объектами datapath (аналог моста/ bridge):

void ovs_lock(void)
void ovs_unlock(void)

#ifdef CONFIG_LOCKDEP
int lockdep_ovsl_is_held(void)
#else
#define lockdep_ovsl_is_held()	1

const char *ovs_dp_name(const struct datapath *dp)
struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)
void ovs_dp_detach_port(struct vport *p)
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_key *key,
		  const struct dp_upcall_info *upcall_info, uint32_t cutlen)
struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
					 u32 portid, u32 seq, u8 cmd)

static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
static const struct genl_small_ops dp_packet_genl_ops[] = {
	{ .cmd = OVS_PACKET_CMD_EXECUTE,
	  .doit = ovs_packet_cmd_execute
	}
};
static struct genl_family dp_packet_genl_family __ro_after_init = {
	.name = OVS_PACKET_FAMILY,
	.small_ops = dp_packet_genl_ops,
};

static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
static const struct genl_small_ops dp_flow_genl_ops[] = {
	{ .cmd = OVS_FLOW_CMD_NEW,
	  .doit = ovs_flow_cmd_new
	},
	{ .cmd = OVS_FLOW_CMD_DEL,
	  .doit = ovs_flow_cmd_del
	},
	{ .cmd = OVS_FLOW_CMD_GET,
	  .doit = ovs_flow_cmd_get,
	  .dumpit = ovs_flow_cmd_dump
	},
	{ .cmd = OVS_FLOW_CMD_SET,
	  .doit = ovs_flow_cmd_set,
	},
};
static struct genl_family dp_flow_genl_family __ro_after_init = {
	.name = OVS_FLOW_FAMILY,
	.small_ops = dp_flow_genl_ops,
};

static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
static const struct genl_small_ops dp_datapath_genl_ops[] = {
	{ .cmd = OVS_DP_CMD_NEW,
	  .doit = ovs_dp_cmd_new
	},
	{ .cmd = OVS_DP_CMD_DEL,
	  .doit = ovs_dp_cmd_del
	},
	{ .cmd = OVS_DP_CMD_GET,
	  .doit = ovs_dp_cmd_get,
	  .dumpit = ovs_dp_cmd_dump
	},
	{ .cmd = OVS_DP_CMD_SET,
	  .doit = ovs_dp_cmd_set,
	},
};
static struct genl_family dp_datapath_genl_family __ro_after_init = {
	.name = OVS_DATAPATH_FAMILY,
	.small_ops = dp_datapath_genl_ops,
};

static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
static const struct genl_small_ops dp_vport_genl_ops[] = {
	{ .cmd = OVS_VPORT_CMD_NEW,
	  .doit = ovs_vport_cmd_new
	},
	{ .cmd = OVS_VPORT_CMD_DEL,
	  .doit = ovs_vport_cmd_del
	},
	{ .cmd = OVS_VPORT_CMD_GET,
	  .doit = ovs_vport_cmd_get,
	  .dumpit = ovs_vport_cmd_dump
	},
	{ .cmd = OVS_VPORT_CMD_SET,
	  .doit = ovs_vport_cmd_set,
	},
};
struct genl_family dp_vport_genl_family __ro_after_init = {
	.name = OVS_VPORT_FAMILY,
	.small_ops = dp_vport_genl_ops,
};

static void ovs_dp_masks_rebalance(struct work_struct *work)
static int __net_init ovs_init_net(struct net *net)
{
	INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq); // dp_notify.c
	INIT_DELAYED_WORK(&ovs_net->masks_rebalance, ovs_dp_masks_rebalance);
}

Функциональные указатели структуры struct genl_small_ops доступны через интерфейс Generic Netlink (net/netlink/genetlink.c) и могут являются поверхностью атаки при вызовах из пользовательского пространства.

С сетевыми данными работают функции ovs_dp_upcall (используется для передачи пакета в пользовательское пространство по протоколу netlink) и ovs_dp_process_packet. Обе функции являются поверхностью атаки для обоих направлений трафика.

datapath.h
static inline struct net *ovs_dp_get_net(const struct datapath *dp)
static inline void ovs_dp_set_net(struct datapath *dp, struct net *net)
static inline struct vport *ovs_vport_rcu(const struct datapath *dp, int port_no)
static inline struct vport *ovs_vport_ovsl_rcu(const struct datapath *dp, int port_no)
static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_no)
static inline struct datapath *get_dp_rcu(struct net *net, int dp_ifindex)
static inline struct datapath *get_dp(struct net *net, int dp_ifindex)

Функции с сетевыми данными не работают.

dp_notify.c
void ovs_dp_notify_wq(struct work_struct *work)
static int dp_device_event(struct notifier_block *unused, unsigned long event, void *ptr)

struct notifier_block ovs_dp_device_notifier = {
	.notifier_call = dp_device_event
};

Функции с сетевыми данными не работают.

flow.c

Управление потоками:

u64 ovs_flow_used_time(unsigned long flow_jiffies)
void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, const struct sk_buff *skb)
void ovs_flow_stats_get(const struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
			unsigned long *used, __be16 *tcp_flags)
void ovs_flow_stats_clear(struct sw_flow *flow)
int ovs_flow_key_update_l3l4(struct sk_buff *skb, struct sw_flow_key *key)
int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
			 struct sk_buff *skb, struct sw_flow_key *key)
int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,   struct sk_buff *skb,
				   struct sw_flow_key *key, bool log)

Функция ovs_flow_key_extract_userspace работает с исходящим трафиком, функции ovs_flow_key_extract и ovs_flow_key_update_l3l4 работают с входящим трафиком, функции ovs_flow_key_update и ovs_flow_stats_update используются в обоих направлениях.

flow.h

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

static inline bool sw_flow_key_is_nd(const struct sw_flow_key *key)
static inline u8 ovs_key_mac_proto(const struct sw_flow_key *key)
static inline u16 __ovs_mac_header_len(u8 mac_proto)
static inline u16 ovs_mac_header_len(const struct sw_flow_key *key)
static inline bool ovs_identifier_is_ufid(const struct sw_flow_id *sfid)
static inline bool ovs_identifier_is_key(const struct sw_flow_id *sfid)

Функция ovs_mac_header_len работает с исходящим трафиком, функция sw_flow_key_is_nd используется в обоих направлениях трафика.

flow_netlink.c

Поддержка протокола netlink:

size_t ovs_tun_key_attr_size(void)
size_t ovs_key_attr_size(void)
int parse_flow_nlattrs(const struct nlattr *attr, const struct nlattr *a[], u64 *attrsp, bool log)
int ovs_nla_put_tunnel_info(struct sk_buff *skb, struct ip_tunnel_info *tun_info)
int nsh_hdr_from_nlattr(const struct nlattr *attr, struct nshhdr *nh, size_t size)
int nsh_key_from_nlattr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
			struct ovs_key_nsh *nsh_mask)
int ovs_nla_get_match(struct net *net, struct sw_flow_match *match, const struct nlattr *nla_key,
		      const struct nlattr *nla_mask, bool log)
bool ovs_nla_get_ufid(struct sw_flow_id *sfid, const struct nlattr *attr, bool log)
int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid,
			   const struct sw_flow_key *key, bool log)
u32 ovs_nla_get_ufid_flags(const struct nlattr *attr)
int ovs_nla_get_flow_metadata(struct net *net, const struct nlattr *a[OVS_KEY_ATTR_MAX + 1],
			      u64 attrs, struct sw_flow_key *key, bool log)
int ovs_nla_put_key(const struct sw_flow_key *swkey, const struct sw_flow_key *output,
		    int attr, bool is_mask, struct sk_buff *skb)
int ovs_nla_put_identifier(const struct sw_flow *flow, struct sk_buff *skb)
int ovs_nla_put_masked_key(const struct sw_flow *flow, struct sk_buff *skb)
int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb)
void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *sf_acts)
int ovs_nla_add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len, bool log)
void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key, bool reset_key,
		    struct sw_flow_mask *mask)
int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key,
			 struct sw_flow_actions **sfa, bool log)
int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb)

Большинство функций доступны при вызовах из пользовательского пространства через структуру struct genl_family (include/net/genetlink.h) интерфейса "generic netlink" (net/netlink/genetlink.c).

С сетевыми данными работает функция ovs_nla_put_key, которая является поверхностью атаки для обоих направлений трафика.

flow_table.c

Управление таблицами потоков

void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
		       bool full, const struct sw_flow_mask *mask)
struct sw_flow *ovs_flow_alloc(void)
int ovs_flow_tbl_count(const struct flow_table *table)
void ovs_flow_free(struct sw_flow *flow, bool deferred)
int ovs_flow_tbl_masks_cache_resize(struct flow_table *table, u32 size)
int ovs_flow_tbl_init(struct flow_table *table)
void table_instance_flow_flush(struct flow_table *table, struct table_instance *ti, 
	       	       	       struct table_instance *ufid_ti)
void ovs_flow_tbl_destroy(struct flow_table *table)
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, u32 *bucket, u32 *last)
int ovs_flow_tbl_flush(struct flow_table *flow_table)
struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl, const struct sw_flow_key *key,
					  u32 skb_hash, u32 *n_mask_hit, u32 *n_cache_hit)
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, const struct sw_flow_key *key)
struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl, const struct sw_flow_match *match)
bool ovs_flow_cmp(const struct sw_flow *flow, const struct sw_flow_match *match)
struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *tbl, const struct sw_flow_id *ufid)
int ovs_flow_tbl_num_masks(const struct flow_table *table)
u32 ovs_flow_tbl_masks_cache_size(const struct flow_table *table)
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
			const struct sw_flow_mask *mask)
void ovs_flow_masks_rebalance(struct flow_table *table)
int ovs_flow_init(void)
void ovs_flow_exit(void)

С сетевыми данными работают функции ovs_flow_mask_key и ovs_flow_tbl_lookup_stats. Обе функции являются поверхностью атаки для обоих направлений трафика.

meter.c

Сбор статистики:

static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info)
static int ovs_meter_cmd_set(struct sk_buff *skb, struct genl_info *info)
static int ovs_meter_cmd_get(struct sk_buff *skb, struct genl_info *info)
static int ovs_meter_cmd_del(struct sk_buff *skb, struct genl_info *info)

static const struct genl_small_ops dp_meter_genl_ops[] = {
	{ .cmd = OVS_METER_CMD_FEATURES,
		.doit = ovs_meter_cmd_features
	},
	{ .cmd = OVS_METER_CMD_SET,
		.doit = ovs_meter_cmd_set,
	},
	{ .cmd = OVS_METER_CMD_GET,
		.doit = ovs_meter_cmd_get,
	},
	{ .cmd = OVS_METER_CMD_DEL,
		.doit = ovs_meter_cmd_del
	},
}; 

struct genl_family dp_meter_genl_family __ro_after_init = {
	.name = OVS_METER_FAMILY,
	.small_ops = dp_meter_genl_ops,
};

bool ovs_meter_execute(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, u32 meter_id)
int ovs_meters_init(struct datapath *dp)
void ovs_meters_exit(struct datapath *dp)

Функциональные указатели структуры struct genl_small_ops доступны через интерфейс Generic Netlink (net/netlink/genetlink.c) и могут являются поверхностью атаки при вызовах из пользовательского пространства.

Функция ovs_meter_execute использует поле skb->len для сбора статистики о трафике, поверхностью атаки не является.

vport.c

Управление виртуальными портами:

int __ovs_vport_ops_register(struct vport_ops *ops)
EXPORT_SYMBOL_GPL(__ovs_vport_ops_register);
void ovs_vport_ops_unregister(struct vport_ops *ops)
EXPORT_SYMBOL_GPL(ovs_vport_ops_unregister);
struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, const struct vport_parms *parms)
EXPORT_SYMBOL_GPL(ovs_vport_alloc);
void ovs_vport_free(struct vport *vport)
EXPORT_SYMBOL_GPL(ovs_vport_free);

int ovs_vport_init(void)
void ovs_vport_exit(void)
struct vport *ovs_vport_locate(const struct net *net, const char *name)
struct vport *ovs_vport_add(const struct vport_parms *parms)
int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
void ovs_vport_del(struct vport *vport)
void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids)
int ovs_vport_get_upcall_portids(const struct vport *vport, struct sk_buff *skb)
u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb)
int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, const struct ip_tunnel_info *tun_info)
void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto)

Функция ovs_vport_receive работает с исходящим трафиком, функция ovs_vport_send работает с входящим трафиком, функция ovs_vport_find_upcall_portid используется в обоих направлениях. Функции ovs_vport_set_options и ovs_vport_set_upcall_portids доступны из пользовательского пространства через протокол netlink.

vport-geneve.c

Поддержка Geneve vport:

static struct vport *geneve_create(const struct vport_parms *parms)
static int geneve_get_options(const struct vport *vport,

static struct vport_ops ovs_geneve_vport_ops = {
	.type		= OVS_VPORT_TYPE_GENEVE,
	.create		= geneve_create,
	.destroy		= ovs_netdev_tunnel_destroy,  // net/openvswitch/vport-netdev.c
	.get_options	= geneve_get_options,
	.send		= dev_queue_xmit,  // net/core/dev.c
};

С сетевыми данными никто не работает.

vport-gre.c

Поддержка GRE vport:

static struct vport *gre_create(const struct vport_parms *parms)

static struct vport_ops ovs_gre_vport_ops = {
	.type		= OVS_VPORT_TYPE_GRE,
	.create		= gre_create,
	.send		= dev_queue_xmit,  // net/core/dev.c
	.destroy		= ovs_netdev_tunnel_destroy,  // net/openvswitch/vport-netdev.c
};

С сетевыми данными функция не работает.

vport-internal_dev.c
static netdev_tx_t internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
static int internal_dev_open(struct net_device *netdev)
static int internal_dev_stop(struct net_device *netdev)
static void internal_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)

static const struct net_device_ops internal_dev_netdev_ops = {
	.ndo_open = internal_dev_open,
	.ndo_stop = internal_dev_stop,
	.ndo_start_xmit = internal_dev_xmit,
	.ndo_set_mac_address = eth_mac_addr,
	.ndo_get_stats64 = internal_get_stats,
};

static void internal_dev_getinfo(struct net_device *netdev, struct ethtool_drvinfo *info)

static const struct ethtool_ops internal_dev_ethtool_ops = {
	.get_drvinfo	= internal_dev_getinfo,
	.get_link	= ethtool_op_get_link,
};

static struct vport *internal_dev_create(const struct vport_parms *parms)
static void internal_dev_destroy(struct vport *vport)
static netdev_tx_t internal_dev_recv(struct sk_buff *skb)

static struct vport_ops ovs_internal_vport_ops = {
	.type		= OVS_VPORT_TYPE_INTERNAL,
	.create		= internal_dev_create,
	.destroy		= internal_dev_destroy,
	.send		= internal_dev_recv,
};

int ovs_is_internal_dev(const struct net_device *netdev)
struct vport *ovs_internal_dev_get_vport(struct net_device *netdev)
int ovs_internal_dev_rtnl_link_register(void)
void ovs_internal_dev_rtnl_link_unregister(void)

Функции internal_dev_xmit и internal_dev_recv работают с исходящим трафиком и являются поверхностью атаки.

Функция ovs_internal_dev_rtnl_link_register регистрирует в системе "Routing netlink socket interface" тип "openvswitch", который соответствует устройствам vport типа OVS_VPORT_TYPE_INTERNAL. Возможности доступа через данный интерфейс из пользовательского пространства требуют дальшейшего изучения.

vport-netdev.c
struct vport *ovs_netdev_link(struct vport *vport, const char *name)
EXPORT_SYMBOL_GPL(ovs_netdev_link);
void ovs_netdev_tunnel_destroy(struct vport *vport)
EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy);

static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
void ovs_netdev_detach_dev(struct vport *vport)
struct vport *ovs_netdev_get_vport(struct net_device *dev)
int __init ovs_netdev_init(void)
void ovs_netdev_exit(void)

static struct vport *netdev_create(const struct vport_parms *parms)
static void netdev_destroy(struct vport *vport)

static struct vport_ops ovs_netdev_vport_ops = {
	.type		= OVS_VPORT_TYPE_NETDEV,
	.create		= netdev_create,
	.destroy		= netdev_destroy,
	.send		= dev_queue_xmit,   // net/core/dev.c
};

С сетевыми данными работает единственная функция netdev_frame_hook, которая обрабатывает входящий трафик и является поверхностью атаки.

vport-vxlan.c

Поддержка VXLAN vport:

static struct vport *vxlan_create(const struct vport_parms *parms)
static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)

static struct vport_ops ovs_vxlan_netdev_vport_ops = {
	.type		= OVS_VPORT_TYPE_VXLAN,
	.create		= vxlan_create,
	.destroy		= ovs_netdev_tunnel_destroy,   // net/openvswitch/vport-netdev.c
	.get_options	= vxlan_get_options,
	.send		= dev_queue_xmit,              // net/core/dev.c
};

Функции с сетевыми данными не работают.

Заключение

Проведенный анализ модуля Openvswitch (net/openvswitch) показал, что 5 функций этого модуля представляют поверхность атаки для входящего сетевого трафика, 5 – используются для исходящего трафика, 13 – могут использоваться в обоих направлениях. Кроме этого, 41 функция являются поверхностью атаки при вызовах из пользовательского пространства (данные функции оперируют не сетевыми данными, а настройками сетевой подсистемы Linux).