- Виртуальный сетевой интерфейс
- О интерфейсах в пару слов
- Модуль виртуального интерфейса
- Как это работает?
- Что дальше?
- Создание и настройка виртуальных сетевых интерфейсов в Linux
- 1. Введение
- 2. Временный виртуальный сетевой интерфейс
- 2.1. Отключение виртуального сетевого интерфейса
- 3. Присвоение виртуальному интерфейсу постоянного адреса
- 3.1. Debian / Ubuntu
- 3.1.1. Статический адрес
- 3.1.2. Dhcp
- 3.2. Redhat / Fedora / CentOS
- 3.2.1. Статический адрес
- 3.2.2. Dhcp
- 4. Заключение
- mtds / lvn.md
Виртуальный сетевой интерфейс
Общеизвестно, что драйверы Linux — это модули ядра. Все драйверы являются модулями, но не все модули — драйверы. Примером одной из таких групп модулей, не являющихся драйверами, и гораздо реже появляющиеся в обсуждениях, являются сетевые фильтры на различных уровнях сетевого стека Linux.
Иногда, и даже достаточно часто, хотелось бы иметь сетевой интерфейс, который мог бы оперировать с трафиком любого другого интерфейса, но каким-то образом дополнительно «окрашивать» этот трафик. Такое может понадобится для дополнительного анализа, или контроля трафика, или его шифрования, …
Идея крайне проста: канализировать трафик уже существующего сетевого интерфейса во вновь создаваемый интерфейс с совершенно другими характеристиками (имя, IP, маска, подсеть, …). Один из способов выполнения таких действий в форме модуля ядра Linux мы и обсудим (он не единственный, но другие способы мы обсудим отдельно в другой раз).
О интерфейсах в пару слов
Сразу понятно, что мы намереваемся «навесить» новый интерфейс, который предстоит создать, на ранее существующий. Поэтому, бегло вспомним то, что касается создания интерфейсов (и что делается, например, драйвером любого сетевого адаптера), потому как там есть несколько нюансов, важных для наших целей.
Сетевой интерфейс — это то место, где:
- по каждому принятому из интерфейса пакету создаются экземпляры структуры сокетных буферов (struct sk_buff), далее созданный экземпляр структуры продвигается по стеку протоколов вверх, до его получателя в пространстве пользователя, где он и уничтожается;
- порождённые где-то на верхних уровнях протоколов пользовательского пространства исходящие экземпляры структуры struct sk_buff должны быть отправлены, а сами экземпляры структуры после этого уничтожаются (или утилизируются в пул).
На протяжении, как минимум, 5-6 последних лет, сетевые интерфейсы неизменно создавались макросом:
Здесь (детали будут понятны из примера модуля):
— sizeof_priv — размер приватной области данных интерфейса (struct net_device), которая будет создана ядром без нашего прямого участия;
— name — символьная строка — шаблон имени интерфейса;
— setup — адрес функции инициализации интерфейса;
В таком, практически неизменном, виде процесс создания интерфейса описан везде в публикациях и упоминается в обсуждениях. Но начиная с ядра 3.17 прототип макроса создания интерфейса меняется (
):
Как легко видеть, теперь вместо 3-х параметров 4, 3-й из которых — константа, определяющая порядок нумерации создаваемых интерфейсов (исходя из шаблона имени), описанная в том же файле определений:
Это первая тонкость на которую следует обратить внимание. Детальнее мы не будем углубляться в эти детали, важно было только отметить их.
Но созданный так интерфейс ещё не дееспособен, он не выполняет никаких действий. Для того, чтобы «придать жизнь» созданному сетевому интерфейсу, нужно реализовать для него соответствующий набор операций. Вся связь сетевого интерфейса с выполняемыми на нём операциями осуществляется через таблицу операций сетевого интерфейса:
В ядре 3.09, например, определено 39 операций в struct net_device_ops, и около 50-ти операций в ядре 3.14, но реально разрабатываемые модули реализуют только малую часть из них.
Характерно, что в таблице операций интерфейса присутствует операция передачи сокетного буфера ndo_start_xmit() в физическую среду, но вовсе нет операции приёма пакетов (сокетных буферов). Это совершенно естественно, как мы увидим вскоре: принятые пакеты (например в обработчике аппаратного прерывания IRQ) непосредственно после приёма вызовом netif_rx() (или netif_receive_skb()) тут же помещаются в очередь (ядра) принимаемых пакетов, и далее уже последовательно обрабатываются сетевым стеком. А вот выполнять функцию ndo_start_xmit() — обязательно, хотя бы, как минимум, для вызова API ядра dev_kfree_skb(), который утилизирует (уничтожает) сокетный буфер после успешной (да и безуспешной тоже) операции передачи пакета. Если этого не делать, в системе возникнет слабо выраженная утечка памяти (с каждым пакетом), которая, в конечном итоге, рано или поздно приведёт к краху системы. Это ещё одна тонкость, которую держим в уме.
Последним необходимым нам элементом является структура struct net_device (описана в
) — описание сетевого интерфейса. Это крупная структура, содержащая не только описание аппаратных средств, но и конфигурационные параметры сетевого интерфейса по отношению к выше лежащим протоколам (пример взят из ядра 3.09):
Здесь поле type, например, определяет тип аппаратного адаптера с точки зрения ARP-механизма разрешения MAC адресов (
):
Со структурой сетевого интерфейса обычно создаётся и связывается приватная структура данных (упоминавшаяся ранее), в которой пользователь может размещать произвольные собственные данные любой сложности, ассоциированные с интерфейсом. Это особо актуально, если предполагается, что драйвер может создавать несколько однотипных сетевых интерфейсов. Доступ к приватной структуре данных должен определяться исключительно специально определённой для того функцией netdev_priv(). Ниже показан возможный вид функции — это определение из ядра 3.09, но никто не даст гарантий, что в другом ядре оно радикально не поменяется:
Как легко видеть из определения, приватная структура данных дописывается непосредственно в хвост struct net_device — это обычная практика создания структур переменного размера, принятая в языке C начиная с стандарта C89 (и в C99).
Этого строительного материала нам будет достаточно для построения модуля виртуального сетевого интерфейса.
Модуль виртуального интерфейса
Здесь всё достаточно просто, но некоторых отдельных комментариев заслуживают следующие моменты:
- После создания интерфейса alloc_netdev() мы связываем его операции через таблицу crypto_net_device_ops. Здесь определены операции (поля): .ndo_open и .ndo_stop (которые вызываются при запуске и остановке интерфейса командой ifconfig up/down), .ndo_get_stats (запрос статистики интерфейса) и .ndo_start_xmit (передача пакета).
- Через приватную область данных мы сохраняем связь с родительским интерфейсом в нами определённой структуре struct priv (в файлах примеров показано несколько различных вариантов использования приватной области для связывания).
- В таблице операций нет (да и быть не может по логике) функции приёма сокетных буферов. Но вызовом netdev_rx_handler_register() (который появился только в ядре 2.6.36) мы можем добавить в очередь обработки принимаемых пакетов (для родительского интерфейса) собственную функцию-фильтр handle_frame(), которая будет вызываться для каждого приходящего с этого интерфейса пакета.
- На время добавления фильтра к очереди, нам необходимо кратковременно заблокировать доступ к очереди (иначе нас может ожидать аварийный результат). Это достигается вызовами rtnl_lock() и rtnl_unlock().
- При передаче исходящего сокетного буфера в сеть (функция start_xmit()) мы просто подменяем в структуре сокетного буфера интерфейс, через который физически должна производиться отправка.
- При приёме, наоборот, сокетные буфера, создаваемые в родительском интерфейсе, подменяются на виртуальный.
Как это работает?
Выберем любой существующий и работоспособный сетевой интерфейс (в Fedora 16 один из Ethernet интерфейсов назывался как p7p1 — это хорошая иллюстрация того, что интерфейсы могут иметь очень разнообразные имена):
Установим на него свой новый виртуальный интерфейс и конфигурируем его на IP подсеть (192.168.50.0/24), отличную от исходной подсети интерфейса p7p1:
Самый простой и быстрый способ создать ответный конец коммуникации (нам ведь нужно как-то тестировать свою работу?) для такой новой (192.168.50.2/24) подсети на другом хосте LAN, это создать алиасный IP для сетевого интерфейса этого удалённого хоста, по типу:
(Здесь показан сетевой интерфейс гипервизора виртуальных машин VirtualBox, но это не имеет значения, и точно то же можно проделать и с интерфейсом любого физического устройства).
Теперь из вновь созданного виртуального интерфейса мы можем проверить прозрачность сети посылкой ICMP:
И далее создать (теперь уже наоборот, на удалённом хосте) полноценную сессию SSH к новому виртуальному интерфейсу:
С таким, вновь созданным, виртуальным интерфейсом можно проделать множество увлекательных экспериментов в самых разнообразных сетевых конфигурациях!
Что дальше?
Проницательный читатель, да ещё если он внимательно читал предыдущий текст, вправе в этом месте воскликнуть: «Но ведь ваш виртуальный интерфейс не дополняет, а замещает родительский?». Да, в показанном варианте именно так: загрузка такого модуля запрещает трафик по родительскому интерфейсу, но выгрузка модуля опять восстанавливает его.
Этому несчастью легко помочь. Для того чтобы создаваемый виртуальный сетевой интерфейс мог работать независимо в дополнение к родительскому, необходимо:
- В фильтрах (и приёма и передачи) анализировать поле IP-адреса в структуре сокетного буфере и производить подмену интерфейса только для IP, принадлежащего виртуальному интерфейсу.
- На приёме разделить обработку сокетных буферов, соответствующим протоколам IP и ARP, потому как структуры данных этих протоколов, естественно, отличаются (поле struct sk_buff*->protocol).
Это выглядит, возможно, сложновато в словесном описании, но в коде модуля всё достаточно просто, и добавляет не более 25 строк кода. И такой вариант приведен в архиве примеров (подкаталог virt-full, здесь этот код не приводится, чтобы не перегружать текст):
Архив кодов для продолжения экспериментирования можете взять здесь или здесь.
Источник
Создание и настройка виртуальных сетевых интерфейсов в Linux
1. Введение
Знаете ли вы, что можете присвоить более чем один IP-адрес физическому сетевому интерфейсу? Эта техника очень полезна, например при работе с Apache и виртуальными хостами, так как позволяет получить доступ к одному и тому же серверу Apache с двух разных IP-адресов.
2. Временный виртуальный сетевой интерфейс
Процесс создания виртуального сетевого интерфейса в Linux не занимает много времени. Он включает один запуск команды ifconfig.
Приведенная выше команда создает виртуальный сетевой интерфейс, базирующийся на оригинальном физическом сетевом интерфейсе eth0. Самое важное условие для создания виртуального сетевого интерфейса — должен существовать физический сетевой интерфейс, в нашем случае eth0. Ниже приведен полный пример:
Теперь мы можем настроить новый виртуальный интерфейс на базе eth0. После выполнения команды ifconfig новый виртуальный интерфейс готов к немедленному использованию.
2.1. Отключение виртуального сетевого интерфейса
Для отключения нашего, созданного ранее, временного сетевого интерфейса мы можем также использовать команду ifconfig с флагом down.
3. Присвоение виртуальному интерфейсу постоянного адреса
Описанные выше настройки не сохраняются после перезагрузки. Если вы хотите, чтобы виртуальный сетевой интерфейс работал постоянно, необходимо модифицировать конфигурационные файлы в соответствии с требованиями вашего дистрибутива Linux. Ниже описан этот процесс для самых распространенных дистрибутивов:
3.1. Debian / Ubuntu
3.1.1. Статический адрес
В Debian или Ubuntu вам необходимо отредактировать файл /etc/network/interfaces, добавив в него следующие строки:
3.1.2. Dhcp
Возможно также использовать витруальный сетевой интерфейс с DHCP. В этом случае вам необходимо добавить в /etc/network/interfaces следующую строку:
Для того, чтобы изменения вступили в силу, необходимо перезапустить сеть:
3.2. Redhat / Fedora / CentOS
3.2.1. Статический адрес
В Redhat, Fedora или CentOS Linux директория, отвечающая за присвоение постоянных IP-адресов — это /etc/sysconfig/network-scripts. В этой директории необходимо создать файл, соответствующий вашему новому виртуальному интерфейсу. В нашем случае этот файл будет называться ifcfg-eth0:0. Создайте этот новый файл и вставьте в него приведенные ниже строки. После перезагрузки адрес будет присвоен виртуальному интерфейсу на постоянной основе.
3.2.2. Dhcp
Когда закончите, перезапустите ваши интерфейсы:
4. Заключение
Раньше один физический сервер обслуживал один веб-сайт. Сегодня такой способ хостинга уже не является жизнеспособным, поэтому способность операционной системы создавать виртуальные сетевые интерфейсы действительно необходима.
Источник
mtds / lvn.md
Virtual Networking on Linux
In the Linux Kernel, support for networking hardware and the methods to interact with these devices is standardized by the socket API:
In order to support new kind of computational workloads, different deployment scenarios and a better use of HW resources the Linux OS supports virtualization of different computing resources: CPU, memory, storage and networking. Virtual networking capabilities are indeed used as a basis for hosting VMs and containers.
A general overview of virtual networking components available in Linux is described in this article from the IBM developerworks web site.
Types of virtual Network Interfaces
- Bridge: A Linux bridge behaves like a network switch. It forwards packets between interfaces that are connected to it. It’s usually used for forwarding packets on routers, on gateways, or between VMs and network namespaces on a host. It also supports STP, VLAN filter, and multicast snooping.
- TUN: TUN (network Tunnel) devices work at the IP level or layer three level of the network stack and are usually point-to-point connections. A typical use for a TUN device is establishing VPN connections since it gives the VPN software a chance to encrypt the data before it gets put on the wire. Since a TUN device works at layer three it can only accept IP packets and in some cases only IPv4. If you need to run any other protocol over a TUN device you’re out of luck. Additionally because TUN devices work at layer three they can’t be used in bridges and don’t typically support broadcasting.
- TAP: TAP (terminal access point) devices, in contrast, work at the Ethernet level or layer two and therefore behave very much like a real network adaptor. Since they are running at layer two they can transport any layer three protocol and aren’t limited to point-to-point connections. TAP devices can be part of a bridge and are commonly used in virtualization systems to provide virtual network adaptors to multiple guest machines. Since TAP devices work at layer two they will forward broadcast traffic which normally makes them a poor choice for VPN connections as the VPN link is typically much narrower than a LAN network (and usually more expensive).
- VETH: Virtual Ethernet interfaces are essentially a virtual equivalent of a patch cable, what goes in one end comes out the other. When either device is down, the link state of the pair is down.
An example of creating a bridge:
Enslaving a network interface to a bridge:
An example of creating two virtual ethernet interfaces (ep1,ep2) and linking them together:
veth interfaces can also be linked to a bridge:
It is also possible to add IP addresses to the interfaces, for example:
All the network interfaces available will be shown with: ip address show
Many other types of virtual network interfaces are available, as described in this post from the RedHat developers blog.
Namespaces are a feature available on the Linux kernel which is used as a basis for many software technology like Linux Containers (LXC), Docker and software-defined network (SDN) solutions. It basically allows to define and use multiple virtual instances of the resources available on a host.
Linux namespaces include (additional references are available in the man page):
In particular, network namespaces allow individual containers to have exclusive access to virtual network resources, while each container can be assigned a separate network stack.
Network namespaces allows different processes to have different views of the network and different aspects of networking can be isolated between processes:
- Interfaces: different processes can connect to addresses on different interfaces.
- Routes: since processes can see different addresses from different namespaces, they also need different routes to connect to networks on those interfaces.
- Firewall rules: since these are dependant on the source or target interfaces, you may need different firewall rules in different network namespaces.
Handling of network namespaces are done with the ip command, which is part of the iproute2 package.
NOTE: all the commands in the following examples have to be executed directly by root or with root privileges (e.g. with sudo ).
Create, list and delete a network namespace:
ns1 is a network NS which is completely separated from the default one (which is always available after every Linux boot).
Distinct network namespaces can be connected together using veth interfaces:
Virtual ethernet interfaces can be assigned an IP address, inside a network class
Once the IPs are assigned, the veth interfaces have to be brought in UP state:
An example of running a ping command between the two different namespaces through the veth interfaces:
A network namespace can have its own network interface assigned to it, for example the loopback interface (which is by default always present on new network NS but in DOWN state):
It can also have a separated routing table (note that when the network namespace is initially set, the routing table is empty):
Once a network NS is created, it will shows up in multiple places:
A virtual network with network namespaces and a bridge
Considering the following properties:
- network NS can have their own network routes;
- virtual ethernet interfaces comes in pairs;
- it’s possible to assign a network interface to a different network NS;
it is then possible to build an example of multiple network NSs connected together through a Linux bridge and routing rules inside the same physical host. A bridge device give us the virtual equivalent of a network switch, allowing us to connect multiple interfaces (virtual or not), and have them communicate with each other.
The following is a conceptual schema:
- br-veth<1,2>: veth attached to the bridge
- veth<1,2>: veth part of their respective network NS
First, two network NS will be created:
Then two pairs of veth will be created:
Now two of the new veths will be attached to the network NS ( br-veth is just a convenient naming convention but it does not identify a veth connected to a bridge).
The two veth <1,2>will be shown only in their respective networks NS:
Note: the veth1 is marked as DOWN . The same goes for veth2 .
Assign the IP address 192.168.1.11 with netmask 255.255.255.0 to veth1:
An IP address (of the same network class) will be assigned also to veth2 :
Even when the two veth have assigned IP address they cannot communicate between each other: the reason is that there’s no configured interface on the default network namespace which can send the traffic to and from the two veth interfaces.
Adding a bridge it’s the only way to go further:
It can be verified that the bridge is available:
It’s now the time to connect the other two veth interfaces ( br-veth <1,2>) and attach them to the bridge:
In order to reach the veth interfaces through the routing table of the host itself, the bridge needs an IP address:
The brd string force to set the broadcast address (192.168.1.255), specifying the + symbol (255).
The routing table can be checked in this way:
From the global network NS it’s possible to reach both IP addresses (192.168.1.<11,12>) through a simple ping .
It’s also possible to reach ns2 from ns1, once the proper routing is defined:
And reaching ns2 can be tested in the following way:
If the setup will stop at this point, both the network NS will be basically isolated from the outside world: they can only ping each other (providing the internal route is configured) but cannot reach any other IP outside the 192.168.1.0/24 space.
In order to achieve this result we can use NAT (Network Address Translation) through iptables :
The previous command will specify that on the nat table we are appending ( -A ) a rule to the POSTROUTING chain for the source address specified ( -s ) and the action will be MASQUERADE .
Last but not least, the IP forwarding has to be enabled on the networking stack of the host:
A small test: send some packets to 8.8.8.8:
Источник