Уровни OSI
1 2 3 4 5 6 7 |
1. Физический 2. Канальный 3. Сетевой 4. Транспортный 5. Сеансовый 6. Представительный 7. Прикладной |
Уровни OSI TCP/IP
1 2 3 4 |
1. Канальный (модуль ядра, драйвер устройства и сетевой интерфейс) L2 2. Сетевой(IP, ICMP, IGMP) L3 3. Транспортный (UDP, TCP, SCTP, DCCP) L4 4. Прикладной (http, https, telnet, ftp, e-mail, smtp, pop3, imap4 и т.д.) |
MAC / ARP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
L2 В масштабе сегмента локальной сети аппаратные адаптеры обмениваются пакетами только по MAC-адресам. Отдельного ARP протокола для ipv6 не существует.(NDP) Протоколы канального уровня(L2) - немаршрутизируемые Для выхода за пределы L2 должны быть как то инкапсулированы в пакет маршрутизируемого протокола. Чаще всего этим занимается протокол IP Для этого применяется ARP, RARP для IPV4 и NDP для ipv6. RFC-826 Преобразованием MAC в IP занимается ядро ОС. macchanger - программа для работы с MAC адресами macchanger eth0 -s - показать MAC адрес eth0 inxi - программа покажет ваше железо inxi -Nxxx - покажет вашу сетевую карту inxi -C - ваш процессор inxi -D - ваши диски Посмотреть таблицу MAC: arp -en arp -vn ip -4 neigh show ip -6 neigh show |
IP адреса:
1 2 3 |
L3 Каждый сетевой интерфейс имеет свой IP-адрес. Конечные точки коммуникации знают о друг друге только по их IP адресу |
Маски и подсети:
1 2 3 4 5 6 7 8 |
Разделение на классы A - 0.0.0.0 - 127.255.255.255 - сверх большие подсети B - 128.0.0.0 - 192.255.255.255 - большие подсети C - 192.0.0.0 - 223.255.255.255 - малые подсети D - 224.0.0.0 - 239.255.255.255 - групповые операции E - 240.0.0.0 - 247.255.255.255 - резерв !!! 25 ноября 2019 года В RFC1219 классы с введение общих масок утратили свой смысл кроме групповых операций (класс D) |
Широковещательный и групповой обмен:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Всего существует три типа IP-адресов персональный / unicast широковещательный / broadcast групповой / multicast Широковещательные и групповые запросы применимы только к UDP и SCTP - подобные типы запросов позволяют приложению разослать одно сообщение нескольким получателям одновременно. TCP - протокол, ориентированный на соединение, с его помощью устанавливается соединение между двумя сетевыми интерфейсами (по указанному их IP-адресу с использованием одного адресата на каждом хосте(который идентифицируется по номеру порта)) Широковещательный адрес подсети(subnet-directed broadcast address) имеет идентификатор хоста, определяемый маской и установленный во все единицы (самый старший адрес диапазона адресов подсети, а самый младший адрес этого диапазона есть адрес самой подсети): 192.168.1.5 - host 255.255.255.0 - netmask 192.168.1.255 - broadcast Групповой адрес (multicast group address) состоит из четырех старших битов IP, установленных в 11110, идентификатора группы (28 младших битов, определяющих конкретную группу). 224.0.0.0 до 239.255.255.255 Для работы с мультикас существует специальный протокол IGMP RFC1112 Internet Management Protocol |
Частные адреса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Никогда не маршрутизируются шлюзами IP. Предназначены для организации локальных сетей. 10.0.0.0 - 10.255.255.255 (10/8) 172.16.0.0 - 172.31.255.255 (172.16/12) 192.168.0.0 - 192.168.255.255 (192.168./16) Решение проблемы с передачей трафика из частных сетей: 1. На сетевом уровне NAT, Network Address Translation (еще эту технологи называют Masquerading, Network Masquerading, Native Adress Translation) Для того что бы такие сети могли общается с внешним миром использую такие технологии как NAT При этом хост IP-шлюза подменяет в маршрутизируемых пакетах IP-адрес отправителя, подставляя IP-адрес собственного интерфейса, после получения пакета производится обратная операция. Проекты реализующую NAT (от старых к новым) ipfw, ipfilter, iptables Сейчас чаще всего встретите iptables (nftable) 2. На уровне протоколов прикладного уровня - прокси (proxy и посредники) При этом программа прокси-сервера ретранслирует запрос хоста с частным IP-адресом от своего имени в интерфейс с глобальным IP-адресом squid / tinyproxy |
IPv6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
6июня 2012 года крупнейшие провайдеры единовременно перешли к использованию - параллельно с IPv4 - IPv6 IPv6 имеет длину 128Бит (вместо 32 у IPv4) Важнейшей отличие от IPv4: Здесь нету отличия между глобальными хостами подключенным к интернету и локальными хостами находящимися в лан. Здесь любой хост равнозначно доступен из вне, если он не закрыт фаерволом. Нет масок.(есть префикс, но это другое) IPv6 три типа адресов: unicast - идентификатор одиночного интерфейса. Пакет, посланный по униксту доставляется интерфейсу по указанному адресу. anycast - идентификатор набора интерфейсов Пакет, посланный по эникастному адресу, доставляется одному из интерфейсов, указанному в адресе (зависит от настройки маршрутизации) multicast - идентификатор набора интерфейса(обычно принадлежит разным узлам) Пакет доставляется всем инерфесам, заданным этим адресом. Префикс адреса В отличии от предыдущей версии протокола, в IPv6 не применяются маски подсети, т.к они получились бы очень длинными, - вместо этого используется префикс, который записывается так же через слеш после адреса. Например префикс /64 означает что из 128 битов первые 64 - это сеть, а оставшаяся часть ( 64) -это хосты Сам адрес записываю в шестнадцатеричном виде. [8 хексетов] X [по 16 бит] = [128 битов] Зарезервированные диапазоны: fe80::/10 называются link-local Любой адрес из этого диапазона предназначен для авто конфигурирования ОС Пакеты отправленные с такого адреса, не пропустит ни один маршрутизатор ограничивая их распространение в пределах сегмента локальной сети Протокол ipv6 спроектирован так что link-local адрес быть обязан. 2000://3 уникальные юникаст-адресов в Интернете. Выдает и регулирует IANA. ff00::/8 - это адрес мультикаста. Например можно получить отклик всех интерфейсов ping6 -c2 ff02::1%eno1 Работа с адресами IPv6 Пример адреса: 2001:0DB8:AA10:0001:0000:0000:0000:00FB Можно убрать начальные 0 2001:DB8:AA10:1:0000:0000:0000:FB Можно сократить еще нули 2001:DB8:AA10:1::FB Для петли: 0000:0000:0000:0000:0000:0000:0000:0001 ::1 Локальные адреса Глобальный адрес IPv6 (global address) Действует в интернете. Выдает IANA. Локальные адреса IPv6 (unique local address) Используются внутри организации. Локальные адреса канала связи(link-local address) не маршрутизируются (fe80::/10) Действуют в рамках одного сегмента. Пинг Для использования команды ping6 требуется указывать суфикс интерфейса (%номер_ipa_a или %имя_фейса) ping -6 -c3 fe80::f1:f1%eno1 в качестве cуфикса можно указывать номер интерфейса выдаваемый командой ping ping -6 -c3 fe80::f1:f1%2 curl [221:58c9:9a6:99be:f3d:clac:2b5b:9771] wget https://curl [221:58c9:9a6:99be:f3d:clac:2b5b:9771]:80/index.php |
Адресные переменные в программном коде / функция inet_pton{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Функция inet_pton() преобразовывает символьные изображения IP адреса в структуру адреса int inet_pton(int af, const char *src, void *dst); af - семейство адресов, которые может быть записано только одной из символьных констант: AF_INET или AF_INET6 означает IP-адрес IPv4 или IPv6 (</usr/include/bits/socket.h>) src - символьная строка, в которой записан исходный IP-адрес Для IPv4 записывается в привычной точечной нотации: d.d.d.d, где d десятичное число от 0-255 Для IPv6 несколько форматов, h:h:h:h:h:h:h:h или h:h:h:h:h:h:d.d.d.d, где h - это 4 знаков шестнадцатеричные числа (0-ffff) кроме того, нулевые адресные группы IPv6, как обычно могут пропускаться, например ::FFFF:204.152.189.116; dst - результат преобразования, структура адреса, вид который зависит от семейства адресов: для AF_INET это (определяется в <linux/in.h>): struct in_addr{ __br32 s_addr; }; для AF_INET6 это (определяется в <linux/in6.h>: struct in6_addr P union { __u8 u6_addr8[16] ... } in6_u; В ядре LInux определено весьма много поддерживаемых семейств адресов, что сильно усложняет поиск информации: cat socket.h | grep 'AF_' | wc -l Возвращаемые значения функции inet_pton: 1 = успешное преобразование. 0 = Строка src не содержит строчное представление в специфицированном семействе адресов. -1 = в качестве параметра af указано недопустимое семейство адресов, при этом глобальная переменная errno устанавливается в EAFNOSUPPORT |
Адресные переменные в программном коде / функция inet_ntop()
1 2 3 4 5 6 7 8 9 |
Функция inet_ntop делает в точности на оборот что делала функция inet_pton Преобразовывает структуру сетевого адреса в символьное изображение IP-адреса. const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); af - семейство адресов AF_INET или AF_INET6 dst - строка, результат преобразования, это же значение возвращается inet_ntop() при успешном выполнение size - длина запрашиваемой строки результата, специфицируемая при вызове, это может быть символьная константа типа INET6_ADDRSTRLEN В заголовочном файле <arpa/inet.h> представлено еще весьма много полезных функций |
Код программы adr для преобразования ip адреса
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
cat > adr.c << "EOF" #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { unsigned char buf[sizeof(struct in6_addr)]; int domain, s; char str[INET6_ADDRSTRLEN]; if (argc != 3) { fprintf(stderr, "Usage: %s {i4|i6|<num>} string\n", argv[0]); exit(EXIT_FAILURE); } domain = (strcmp(argv[1], "i4") == 0) ? AF_INET : (strcmp(argv[1], "i6") == 0) ? AF_INET6 : atoi(argv[1]); s = inet_pton(domain, argv[2], buf); if (s <= 0) { if (s == 0) fprintf(stderr, "Not in presentation format"); else perror("inet_pton"); exit(EXIT_FAILURE); } if (inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL) { perror("inet_ntop"); exit(EXIT_FAILURE); } printf("%s\n", str); exit(EXIT_SUCCESS); } EOF Для компиляции используй: gcc -o adr adr.c Примеры: ./adr i6 0:0:0:0:0:0:0:0 ./adr i6 1:0:0:0:0:0:0:9 ./adr 2001:DB8:AA10:1:0000:0000:0000:FB ./adr i6 0:0:0:0:0:FFFF:204.152.189.116 ./adr i4 204.152.189.116 |
DNS / NS / разрешение имен
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Человеку сложно запомнить цифры, запомнить слова проще, собственно это и привело к появлению DNS. Domain Name System До зарождения DNS был файл /etc/hosts Заполнялся вручную. Программы: nslookup qnx.org.ru 1.1.1.1 nslookup qnx.org.ru 8.8.8.8 host rus-linux.net dig @8.8.4.4 b14esh.com |
Разрешение имен в программном коде:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
cat > gclie.c << "EOF" #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUF_SIZE 500 int main(int argc, char *argv[]) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s, j; size_t len; ssize_t nread; char buf[BUF_SIZE]; if (argc < 3) { fprintf(stderr, "Usage: %s host port msg...\n", argv[0]); exit(EXIT_FAILURE); } /* Obtain address(es) matching host/port */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ hints.ai_flags = 0; hints.ai_protocol = 0; /* Any protocol */ s = getaddrinfo(argv[1], argv[2], &hints, &result); if (s != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); exit(EXIT_FAILURE); } /* getaddrinfo() returns a list of address structures. Try each address until we successfully connect(2). If socket(2) (or connect(2)) fails, we (close the socket and) try the next address. */ for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; /* Success */ close(sfd); } if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not connect\n"); exit(EXIT_FAILURE); } freeaddrinfo(result); /* No longer needed */ /* Send remaining command-line arguments as separate datagrams, and read responses from server */ for (j = 3; j < argc; j++) { len = strlen(argv[j]) + 1; /* +1 for terminating null byte */ if (len + 1 > BUF_SIZE) { fprintf(stderr, "Ignoring long message in argument %d\n", j); continue; } if (write(sfd, argv[j], len) != len) { fprintf(stderr, "partial/failed write\n"); exit(EXIT_FAILURE); } nread = read(sfd, buf, BUF_SIZE); if (nread == -1) { perror("read"); exit(EXIT_FAILURE); } printf("Received %ld bytes: %s\n", (long)nread, buf); } exit(EXIT_SUCCESS); } EOF cat > gserv.c << "EOF" #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netdb.h> #define BUF_SIZE 500 int main(int argc, char *argv[]) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s; struct sockaddr_storage peer_addr; socklen_t peer_addr_len; ssize_t nread; char buf[BUF_SIZE]; if (argc != 2) { fprintf(stderr, "Usage: %s port\n", argv[0]); exit(EXIT_FAILURE); } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; s = getaddrinfo(NULL, argv[1], &hints, &result); if (s != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); exit(EXIT_FAILURE); } /* getaddrinfo() returns a list of address structures. Try each address until we successfully bind(2). If socket(2) (or bind(2)) fails, we (close the socket and) try the next address. */ for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) break; /* Success */ close (sfd); } if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not bind\n"); exit(EXIT_FAILURE); } freeaddrinfo(result); /* No longer needed */ /* Read datagrams and echo them back to sender */ for (;;) { peer_addr_len = sizeof(struct sockaddr_storage); nread = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr*)&peer_addr, &peer_addr_len); if (nread == -1) continue; /* Ignore failed request */ char host[NI_MAXHOST], service[NI_MAXSERV]; s = getnameinfo((struct sockaddr*) &peer_addr, peer_addr_len, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV); if (s == 0) printf("Received %ld bytes from %s:%s\n", (long)nread, host, service); else fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s)); if (sendto(sfd, buf, nread, 0, (struct sockaddr*)&peer_addr, peer_addr_len) != nread) fprintf(stderr, "Error sending response\n"); } } EOF Для компиляции используй: gcc -o gclie gclie.c gcc -o gserv gserv.c Запуск: ./gserv 6000 ./gclie localhost 6000 privet ./gclie 127.0.0.1 6000 privet Что тут происходи: Функция <netdb.h> #include <netdb.h> struct hostent *gethostbyname(const char *name); Функция возвращает информации о хосте по его имени struct hostent { char *h_name; /* Official name of host. */ char **h_aliases; /* Alias list. */ int h_addrtype; /* Host address type AF_INET or AF_INET6 */ int h_length; /* Length of address 4 or 16 */ char **h_addr_list; /* List of addresses from name server. */ Функция gethostbyaddr() возвращает информацию о хосте по IP-адресу Функция getaddrinfo()ведена более поздним POSIX, скрывает все зависимости в параметрах от типа протокола |
Пример Makefile для adr, gclie, gserv
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
cat > Makefile << "EOF" CC = gcc -Wall SRC = adr gclie gserv all: $(SRC) clean disclean: rm -f $(SRC) EOF # Исправить восемь пробелов на TAB sed -i 's/ /\t\t/g' Makefile Для компиляции используй: make |
Сетевые интерфейсы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
Посмотреть текущие сетевые интерфейсы: ip link ifconfig В каталоге /proc ls /proc/sys/net/ipv4/conf ls /proc/sys/net/ipv4/conf/eth0/ ls -w80 /proc/sys/net/ipv4/conf/eth0/ Так же как для блочных устройств(дисков) в Linux, когда они еще не пригодны для работы, пока их не смонтируют, так и сетевые интерфейсы сами по себе еще не пригодны для работы в сети, пока их не подготовят к использованию. Для работы нужно назначить например IP-адрес, маску. Пример именования интерфейсов и их назначения(ip link / ip a / ifconfig): cipsec0 - Виртуальная частная сеть, VPN, "Cisco Systems VPN Client от Cisco Systems", работает через реальный физический канал. eml - интерфейс физического проводного Ethernet-адаптера eth0- интерфейс физического проводного Ethernet-адаптера enp1- интерфейс физического проводного Ethernet-адаптера wlan0 - интерфейс физического беспроводного Wi-Fi-адаптера wlo1 - интерфейс физического беспроводного Wi-Fi-адаптера ppp0 - это может быть беспроводной 3G CMDA модем на USB-шине lo - логический петлевой интерфейс Проверка доступности ip адреса ping 10.0.0.1 Основные команды для работы c Ethernet ifconfig --help ip help ip a ip addr show dev eth0 ip link ip addr help Основные команды для работы с Wi-Fi iwconfig iw wlan0 info rfkill rfkill list iw phy0 info Графическая программа для управления сетью NetworkManager Консольная программа для управления NetworkManager nmcli --help |
Таблица маршрутизации (роутинг)
1 2 3 4 5 6 7 8 9 |
route -n route -n -6 ip route --help ip route show ip -6 route show Если все рассматриваемые ранее параметры: IP-адрес, маска, префикс и д.р. - это атрибуты сетевого интерфейса, то таблица роутинга - это атрибут сетевого хоста в целом, это таблица ядра операционной системы. 0.0.0.0 - default - шлюз последней надежды |
Алиасные IP-адреса / дополнительный IP адрес из другой сети
1 2 3 4 5 |
ifconfig eno1:1 10.0.0.2/24 up команда ip позволяет сразу навесить скольго угодна ip на один интерфейс. ip a a 10.10.0.1/24 dev eno1 ip a a 10.10.1.1/24 dev eno1 ip a a 10.10.2.1/24 dev eno1 |
Петлевой интерфейс:
1 2 3 4 5 |
На любом компьютере хосте, даже если он никак не использует сеть (не подключен к сети), присутствует петлевой интерфейс (loopback, интерфейс lo) под петлевой интерфейс выделена группа 127/8 и ::1 Петлевой интерфейс позволяет программам на компьютере использовать стек TCP/IP локально. |
Переименование сетевого интерфейса
1 |
ip link set dev eno1 name eth0 |
Альтернативные имена (ядро выше 5.4.0)
1 |
ip link property add dev eth0 altname eno99 |
Порты транспортного уровня:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
Каждому протоколу более высоких уровней (SSH,RDP,FTP,HTTP,HTTPS, etc) соответствует свой стандартный порт. Порты бываю: TCP или UDP Порты разделены на три: общеизвестные / системны 0-1024 (всегда требуются права root) зарегистрированные / пользовательские 1024-49151 (зарегистрировала комиссия IANA официально) динамические / частные 49152-65535 Посмотреть порты: cat /etc/services Посмотреть сколько портов знает система: grep -v ^$ /etc/services | grep -v ^# | wc -l Номера портов можно и используется разные, например для HTTP никто не может веб серверу запретить использовать порт 8888 вместо 80. Датаграммная передача - UDP Потоковая передача - UDP Датаграммный протокол UDP ориентирован на быструю передачу информации, ничем не гарантирующую доставку. Вплоть до того, что приемная сторона(ее сетевой стек) имеет право просто успешно принимать UDP-сообщения, но если она перегружена, то по собственной инициативе просто сбрасывать в мусор уже принятые пакеты, никак не уведомляя об этом ни приемную, ни передающую сторону. Потоковый протокол TCP ориентирован на надежную доставку информации. В случае потери пакетов или их искажения на тракте передающая сторона делает многократные попытки повторения передачи. Протокол UDP определен в RFC 768. Протокол TCP посвящены RFC 790, 791, 793, 1025, 1323. !!! Не обольщайтесь кажущейся простотой и понятностью протокола UDP при реализации надежного взаимодействия в создаваемом вами проекте. Надстраивание над UDP средств контроля и резервирования - это ловушка, которую попадают многие разработчики, впервые начинающие сетевое проектирование. При этом повторяете путь развития TCP, который занял не одно десятилетие. |
Инструменты диагностики:
1 2 3 4 |
посмотреть линки: ip link Проверить таблицу маршрутизации: route -n |
Инструменты наблюдения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
ping 8.8.8.8 traceroute 8.8.8.8 netstat -i netstat -t ss -tplnu arp nslookup google.com nslookup 8.8.8.8 host ya.ru dig google.com tcpdump -i eth0 id who ps -uaxf |
Инструменты тестирования:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
scp test root@10.10.10.10: sftp name@6.6.6.6:/home/name/1.txt nc -l 1234 nc 127.0.0.1 1234 iperf -s iperf -c 10.10.10.1 iperf -c 10.10.10.1 -P4 iperf3 -s iperf3 -c 10.10.10.1 iperf3 -c 10.10.10.1 -P4 |
Сервисы сети и systemd
1 2 3 4 |
Все сетевые продукты состоят из трех основных компонентов 1) Протокол 2) Клиент 3) Сервис |
mc (Midnight Commnader)
1 2 3 4 5 6 7 8 9 10 |
Есть очень удобный способ подключится по ssh к удаленной машине Называется он "Shell-соединение" перед использованием этого способа убедитесь что у вас подключается по ssh. TAB - перемещение между правой и левой лакацией Enter - Свободное перемещение по каталогам F5 - копировать F6 - перемещать F3 - просматривать F4 - редактировать |
SSH
1 2 3 4 5 6 7 |
Графическая сессия ssh -X user@server xclock ssh -Y user@server VirtualBox Не все так может быть запущенно, но многие программы работают. Не которые программы сами умеют подключатся через shell. Например mc, virt-manager и etc |
Протокол DHCP
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DHCP Dynamic Host Configuration Protocol Протокол DHCP - это клиент-серверный протокол, который автоматически доставляет хосту в момент его загрузки IP-адрес (из определенного пула адресов) и другие связанные сведения о конфигурации, такие как маска подсети и шлюз по умолчанию RFC2131 и 2132 - определяют протокол DHCP в качестве стандарта "Internet Engineering Task Force" (IETF) основанного на более раннем протоколе загрузке BOOTP В сети не может быть более одного DHCP сервера, так как могут возникнуть проблемы раздачи IP-адресов Самый популярный в linux DHCP сервер это isc-dhcp-server Чаще всего встретите после него это dnsmasq |
Разрешение имен: служба DNS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Domain name system Сам DNS-протокол базируется на UDP-, либо на TCP-транспорте, на ранних этапах это было только UDP Используется по умолчанию порт 53 (TCP/UDP) Самый известный DNS-резолвер bind Самый просто и кеширующий DHCP/DNS-сервер dnsmasq Самый частый в systemd это systemd-resolved.services (чаще всего выключен, но настраивается очень просто, нужно отредактировать "DNS=" в файле "/etc/systemd/resolved.conf" и включить демон командой "sytemctl enable systemd-resolved.services && sytemctl start systemd-resolved.services") resolvectl status # для debian11-12 может пригодится для восстановления systemd-resolved # ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf |
Немного про конфигурацию dnsmasq
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
ls /etc/dnsmasq.d/*.conf конфиги могут быть разнесены по файла dhcp.conf dns.conf так и могут быть в одном файле dnsmasq.conf dhcp.conf --------- interface=eth1 # - интерфейс на котором будет работать dhcp dhcp-range=192.168.25.2,192.168.25.150,255.255.255.0,12h # - объявляем диапазон для аренды dhcp-option=option:router,192.168.25.1 # - default gateway log-dhcp # - включить логи --------- dns.conf -------- listen-address=127.0.0.1 # - принимаем запросы только из петли interface=eth1 # - слушаем только на этом интерфейсе domain-needed # - никогда не присылать адреса без доменной части bogus-priv # - никогда не пересылать адреса из не маршрутизированного пространства stop-dns-rebind # - отклонять ответы от вышестоящих DNS-серверов rebind-localhost-ok # - отключить проверку для 127.0.0.0/8 strict-order # - пересылать запросы с первого по порядку no-resolv # - не использовать /etc/resolv.conf no-hosts # - не читать /etc/hosts domain=localdomain local-ttl=7200 neg-ttl=14400 max-ttl=86400 server=192.168.25.1 server=8.8.8.8 server=8.8.4.4 -------- |
iptables
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
iptables --help Просмотр правил основных цепочек INPUT \ FORWARD \ OUTPUT iptables -L -v -n iptables-save Просмотр правил трансляции адресов NAT: iptables -t nat -L -line-numbers Разрешить все входящие пакеты (таблицу filter можно не указывать она дефолтная): iptables -f filter -I OUTPUT 1 -j ACCEPT Запретить все исходящие пакеты из подсети 10.10.10.0/24 iptables -t filter -A INPUT -s 10.10.10.0/24 -j DROP Разрешение SSH: iptables -A INPUT -p tcp --dport 22 -J ACCEPT Разрешить диапазон входящих портов TCP: iptables -A -p tcp --dport 3000:4000 -j ACCEPT Разрешить ICMP: iptables -A INPUT -p icmp -j ACCEPT |
Systemd и сокетная активация сервисов на примере pure-ftpd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
!!! Врятли у вас получится сделать это на современных дистрибутивах, так как файлы для запуска сервиса будут установлены автоматически. apt install pure-ftpd cat > /usr/lib/systemd/system/pure-ftpd.socket << "EOF" [Unit] Descrtiption=pure-ftp FTP server Activation Socket Conflicts=pure-ftpd.service [Socket] ListenStream=21 Accept=true [Install] WantedBy=socket.target EOF cat > /usr/lib/systemd/system/pure-ftpd@.service << "EOF" [Unit] Descriptiom=pure-ftpd FTP Server After=network-online.target [Service] ExecStart=-/usr/sbin/pure-ftpd StandartInput=socket EOF systemctl status pure-ftpd systemctl stop pure-ftpd systemctl status pure-ftpd systemct start pure-ftpd.socket systemct status pure-ftpd.socket |
Прокси серверы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
Преодоление текториальных ограничений Защита компьютера клиента от атак с наружи Позволяет сохранить анонимность клиента путем подмены IP Проксирование на уровне протоков HTTP, HTTPS Проксирование на уровне L4 UDP, TCP SOCKS4 и SOCKS5 SOCKS5 - допускает возможность UDP, есть аутентификация Самый распространённый SOCKS5 прокси сервер это Dante apt search Dante apt install dante-client - научит любое приложение ходить в прокси apt install dante-serve - прокси сервер Пока вырубим его systemctl disable danted systemctl stop danted Простой конфиг без авторизации vim /etc/danted.conf -------------------- logoutput: stderr internal: eth0 port = 1080 external: eth0 socksmethod: none user.privileged: proxy user.unprivileged: nobody user.libwrap: nobody client pass { from: 0/0 to: 0/0 log: connect error } socks pass { from: 0/0 to: 0/0 log: connect error } -------------------- тут: internal - входящий интерфейс external - исходящий port - порт прокси socksmethod - собственно нету авторизации Для авторизации изменить конфиг так: socksmethod: username socks pass { from: 0/0 to: 0/0 log: connect disconnect error ioop group: proxy } и создать пользователя useradd --shell /usr/sbin/nologin proxy_user_01 passwd proxy_user_01 usermod -a -G proxy proxy_user_01 |
Прокси через SSH
1 2 3 |
ssh -N -D 5555 10.10.10.22 curl -s -x socks4://127.0.0.1:5555 ya.ru curl -s -x socks5://127.0.0.1:5555 ya.ru |
Прокси squid
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#Debian 12 install squid Устанавливаем: apt install squid Сохраняем оригинальный конфиг squid: cp /etc/squid/squid.conf /etc/squid/squid.conf.backup ( можно вот так вот обработать cat squid.conf.bak | grep -v "^#" | grep -v "^$" > squid.conf тем самым убрав комментарии) Приводим конфиг к такому виду: cat > /etc/squid/squid.conf << "EOF" acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN) acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN) acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN) acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN) acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN) acl localnet src fc00::/7 # RFC 4193 local private network range acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines acl SSL_ports port 443 acl Safe_ports port 80 # http acl Safe_ports port 21 # ftp acl Safe_ports port 443 # https acl Safe_ports port 70 # gopher acl Safe_ports port 210 # wais acl Safe_ports port 1025-65535 # unregistered ports acl Safe_ports port 280 # http-mgmt acl Safe_ports port 488 # gss-http acl Safe_ports port 591 # filemaker acl Safe_ports port 777 # multiling http acl CONNECT method CONNECT http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow localhost manager http_access deny manager include /etc/squid/conf.d/* http_access allow localhost http_access deny all http_port 3128 coredump_dir /var/spool/squid refresh_pattern ^ftp: 1440 20% 10080 refresh_pattern ^gopher: 1440 0% 1440 refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 refresh_pattern . 0 20% 4320 EOF И этот конфиг приводим к такому виду: cat > /etc/squid/conf.d/debian.conf << EOF # # Squid configuration settings for Debian # # Logs are managed by logrotate on Debian logfile_rotate 0 # For extra security Debian packages only allow # localhost to use the proxy on new installs # http_access allow localnet EOF Запускаем в зависимости от статуса: systemctl status squid systemctl restart squid systemctl start squid systemctl enable squid |
Мониторинг за прокси
1 2 3 |
apt install sockstat sockstat -h sockstat -4 |
Фазы соединения TCP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
В формате каждого пакета транспортного уровня TCP предусмотрено несколько битовых флагов, определяющих предназначение пакета. Некоторые флаги могут быть установлены одновременно но не в любых комбинациях. UGR - указатель срочности (urgent pointer) данных ACK - номер подтверждения необходимо принять в рассмотрение (acknowledgment) PSH - получатель должен передать эти данные приложению как можно скорее (push) RST - сбросить соединение (reset) YN - синхронизирующий номер последовательности установления соединения FIN - отправитель закончил пересылку данных Что бы установить TCP соединения, пересылаются три сегмента: 1. Запрашивающая сторона(клиент) отправляет сегмент (флаг) SYN, указывающий номер порта сервера, к которому клиент хочет подключится, и исходный номер последовательности клиента ISN 2. Сервер отвечает своим сегментом SYN, содержащий номер последовательности сервера. Сервер также подтверждает риход SYN от клиента с использованием флага ACK(указыва номер ISN клиента плюс один) 3. Клиент должен подтвердить приход SYN от сервера с использованием флага ACK(ISN сервера плюс один) Этих трех сегментов достаточно для установления соединения. Часто называют трехразовым рукопожатием (three-way handshake). Любая сторона может разорвать соединение послав пакет с флагом FIN. Чаще всего это делает сам клиент. И это также сопровождается трёхразовым рукопожатием. |
Пример работы с UDP \ TCP в коде :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 |
cat > common.h << "EOF" #ifndef __common_h #define __common_h #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/socket.h> /* basic socket definitions */ #include <netinet/in.h> #include <arpa/inet.h> #include "inet.h" #define MAXLINE 4096 /* max text line length */ static char sendline[MAXLINE], /* write buffer */ recvline[MAXLINE + 1]; /* reaad buffer */ void err_dump(const char *, ...); void err_sys(const char *, ...); void str_cli(register FILE*, register int sockfd); void str_echo(int sockfd); void dg_cli(FILE* fp, int sockfd, struct sockaddr* pserv_addr, int servlen); void dg_echo(int sockfd, struct sockaddr* pcli_addr, int maxclilen); #endif EOF cat > dgcli.c << "EOF" /* Read the contents of the FILE *fp, write each line to the * datagram socket, then read a line back from the datagram * socket and write it to the standard output. * Return to caller when an EOF is encountered on the input file. */ #include "inet.h" void dg_cli(FILE* fp, int sockfd, struct sockaddr* pserv_addr, // ptr to appropriate sockaddr_XX structure int servlen) { // actual sizeof(*pserv_addr) int n; while (fgets(sendline, MAXLINE, fp) != NULL) { n = strlen(sendline); if (sendto(sockfd, sendline, n, 0, pserv_addr, servlen) != n) err_dump("dg_cli: sendto error on socket"); // Now read a message from the socket and // write it to our standard output. n = recvfrom(sockfd, recvline, MAXLINE, 0, (struct sockaddr*)0, (socklen_t*)0); if (n < 0) err_dump("dg_cli: recvfrom error"); recvline[n] = 0; /* null terminate */ fputs(recvline, stdout); } if (ferror(fp)) err_dump("dg_cli: error reading file"); } EOF cat > dgecho.c << "EOF" /* Read a datagram from a connectionless socket and write it back to * the sender. * We never return, as we never know when a datagram client is done. */ #include "inet.h" void dg_echo(int sockfd, struct sockaddr* pcli_addr, /* ptr to appropriate sockaddr_XX structure */ int maxclilen) { /* sizeof(*pcli_addr) */ for (;;) { socklen_t clilen = maxclilen; int n = recvfrom(sockfd, recvline, MAXLINE, 0, pcli_addr, &clilen); if (n < 0) err_dump("dg_echo: recvfrom error"); if (sendto(sockfd, recvline, n, 0, pcli_addr, clilen) != n) err_dump("dg_echo: sendto error"); } } cat > udpcli.c << "EOF" /* Example of client using UDP protocol. */ #include "inet.h" int main(int argc, char* argv[]) { int sockfd; /* Open a UDP socket (an Internet datagram socket). */ if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) err_dump("client: can't open datagram socket"); struct sockaddr_in serv_addr; /* Fill in the structure "serv_addr" with the address * of the server that we want to send to. */ bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR); serv_addr.sin_port = htons(SERV_UDP_PORT); dg_cli(stdin, sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); close(sockfd); exit(0); } EOF cat > udpserv.c << "EOF" // Example of server using UDP protocol. #include "inet.h" int main(int argc, char* argv[] ) { /* Open a UDP socket (an Internet datagram socket). */ int sockfd; if((sockfd = socket(AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) err_dump("server: can't open datagram socket" ); /* Bind our local address so that the client can send to us. */ struct sockaddr_in serv_addr; bzero((char*)&serv_addr, sizeof(serv_addr ) ); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY ); serv_addr.sin_port = htons(SERV_UDP_PORT ); if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr ) ) < 0 ) err_dump("server: can't bind local address" ); dg_echo(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr ) ); } EOF cat > error.c << "EOF" #include "common.h" #include <stdarg.h> /* ANSI C header file */ #include <syslog.h> /* for syslog() */ int daemon_proc; /* set nonzero by daemon_init() */ static void err_doit(int, int, const char *, va_list); /* Fatal error related to a system call. * Print a message and terminate. */ void err_sys(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(1, LOG_ERR, fmt, ap); va_end(ap); exit(1); } /* Fatal error related to a system call. * Print a message, dump core, and terminate. */ void err_dump(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(1, LOG_ERR, fmt, ap); va_end(ap); abort(); /* dump core and terminate */ exit(1); /* shouldn't get here */ } /* Print a message and return to caller. * Caller specifies "errnoflag" and "level". */ static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) { int errno_save, n; char buf[MAXLINE]; errno_save = errno; /* value caller might want printed */ #ifdef HAVE_VSNPRINTF vsnprintf(buf, sizeof(buf), fmt, ap); /* this is safe */ #else vsprintf(buf, fmt, ap); /* this is not safe */ #endif n = strlen(buf); if (errnoflag) snprintf(buf + n, sizeof(buf) - n, ": %s", strerror(errno_save)); strcat(buf, "\n"); if (daemon_proc) { syslog(level, buf, "cli-serv : %s"); } else { fflush(stdout); /* in case stdout and stderr are the same */ fputs(buf, stderr); fflush(stderr); } return; } EOF cat > inet.h << "EOF" /* Definitions for TCP and UDP client/server programs. */ #include "common.h" #define SERV_UDP_PORT 60000 #define SERV_TCP_PORT 60000 //#define SERV_HOST_ADDR "192.168.1.13" /* host addr for server */ #define SERV_HOST_ADDR "127.0.0.1" /* host addr for server */ ssize_t readline(int fd, void *vptr, size_t maxlen); ssize_t writen(int fd, const void *vptr, size_t n); EOF cat > Makefile << "EOF" MYLIB = error.o writen.o readline.o #LIBS = CFLAGS = -O -Wall -Wno-unused-variable all: $(MYLIB) tcp udp unixstr unixdg sockopt rm -f *.o lib: $(MYLIB) # Internet stream version (TCP protocol). tcp: tcpserv tcpcli tcpcli.o tcpserv.o: inet.h common.h tcpserv: tcpserv.o strecho.o $(CC) $(CFLAGS) -o $@ tcpserv.o strecho.o $(MYLIB) tcpcli: tcpcli.o strcli.o $(CC) $(CFLAGS) -o $@ tcpcli.o strcli.o $(MYLIB) # Internet datagram version (UDP protocol). udp: udpserv udpcli udpcli.o udpserv.o: inet.h common.h udpserv: udpserv.o dgecho.o $(CC) $(CFLAGS) -o $@ udpserv.o dgecho.o $(MYLIB) udpcli: udpcli.o dgcli.o $(CC) $(CFLAGS) -o $@ udpcli.o dgcli.o $(MYLIB) # UNIX stream version. unixstr: unixstrserv unixstrcli unixstrcli.o unixstrserv.o: unix.h common.h unixstrserv: unixstrserv.o strecho.o $(CC) $(CFLAGS) -o $@ unixstrserv.o strecho.o $(MYLIB) unixstrcli: unixstrcli.o strcli.o $(CC) $(CFLAGS) -o $@ unixstrcli.o strcli.o $(MYLIB) # UNIX datagram version. unixdg: unixdgserv unixdgcli unixdgcli.o unixdgserv.o: unix.h common.h unixdgserv: unixdgserv.o dgecho.o $(CC) $(CFLAGS) -o $@ unixdgserv.o dgecho.o $(MYLIB) unixdgcli: unixdgcli.o dgcli.o $(CC) $(CFLAGS) -o $@ unixdgcli.o dgcli.o $(MYLIB) sockopt: sockopt.o common.h $(CC) $(CFLAGS) -o $@ sockopt.o $(MYLIB) clean disclean: -rm -f *.o core a.out temp*.* \ tcpserv tcpcli udpserv udpcli \ unixstrserv unixstrcli unixdgserv unixdgcli \ s.unixdg s.unixstr sockopt EOF # Исправить восемь пробелов на TAB sed -i 's/ /\t\t/g' Makefile cat > readline.c << "EOF" #include "common.h" static ssize_t my_read(int fd, char *ptr) { static int read_cnt = 0; static char *read_ptr; static char read_buf[MAXLINE]; if (read_cnt <= 0) { again: if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return(-1); } else if (read_cnt == 0) return(0); read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return(1); } ssize_t readline(int fd, void *vptr, size_t maxlen) { int n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ((rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == '\n') break; /* newline is stored, like fgets() */ } else if (rc == 0) { if(n == 1) return(0); /* EOF, no data read */ else break; /* EOF, some data was read */ } else return(-1); /* error, errno set by read() */ } *ptr = 0; /* null terminate like fgets() */ return(n); } EOF cat > sockopt.c << "EOF" /* Example of getsockopt() and setsockopt(). */ #include "common.h" #include <sys/types.h> #include <sys/socket.h> /* for SOL_SOCKET and SO_xx values */ #include <netinet/in.h> /* for IPPROTO_TCP value */ #include <netinet/tcp.h> /* for TCP_MAXSEG value */ int main() { int sockfd, maxseg, sendbuff; //, optlen; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("can't create socket"); /* Fetch and print the TCP maximum segment size. */ socklen_t optlen = sizeof(maxseg); if (getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, (char*)&maxseg, &optlen) < 0) err_sys("TCP_MAXSEG getsockopt error"); printf("TCP maxseg = %d\n", maxseg); /* Set the send buffer size, then fetch it and print its value. */ sendbuff = 16384; /* just some number for example purposes */ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&sendbuff, sizeof(sendbuff)) < 0) err_sys("SO_SNDBUF setsockopt error"); optlen = sizeof(sendbuff); if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&sendbuff, &optlen) < 0) err_sys("SO_SNDBUF getsockopt error"); printf("send buffer size = %d\n", sendbuff); } EOF cat > strcli.c << "EOF" /* Read the contents of the FILE *fp, write each line to the * stream socket (to the server process), then read a line back from * the socket and write it to the standard output. * Return to caller when an EOF is encountered on the input file. */ #include "common.h" void str_cli(register FILE* fp, register int sockfd) { int n; while (fgets(sendline, MAXLINE, fp) != NULL) { n = strlen(sendline); if (writen(sockfd, sendline, n) != n) err_sys("str_cli: writen error on socket"); /* Now read a line from the socket and * write it to our standard output. */ n = readline(sockfd, recvline, MAXLINE); if (n < 0) err_dump("str_cli: readline error"); recvline[n] = 0; /* null terminate */ fputs(recvline, stdout); } if (ferror(fp)) err_sys("str_cli: error reading file"); } EOF cat > strecho.c << "EOF" /* Read a stream socket one line at a time, and write each line back * to the sender. * Return when the connection is terminated. */ #include "common.h" void str_echo(int sockfd) { int n; for (;;) { n = readline(sockfd, recvline, MAXLINE); if (n == 0) return; /* connection terminated */ else if (n < 0) err_dump("str_echo: readline error"); if (writen(sockfd, recvline, n) != n) err_dump("str_echo: writen error"); } } EOF cat > tcpcli.c << "EOF" /* Example of client using TCP protocol. */ #include "inet.h" int main(int argc, char* argv[]) { int sockfd; // TCP сокет struct sockaddr_in serv_addr; // заполнить структуру адреса сервера bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR); serv_addr.sin_port = htons(SERV_TCP_PORT); /* Open a TCP socket (an Internet stream socket). */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("client: can't open stream socket"); /* Connect to the server. */ if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) err_sys("client: can't connect to server"); str_cli(stdin, sockfd); // цикл обменов с сервером close(sockfd); exit(0); } EOF cat > tcpserv.c << "EOF" /* Example of server using TCP protocol. */ #include "inet.h" int main(int argc, char* argv[]) { int sockfd; // TCP сокет if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_dump("server: can't open stream socket"); struct sockaddr_in serv_addr; // инициализировать униадресом bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERV_TCP_PORT); if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) err_dump("server: can't bind local address"); listen(sockfd, 5); for (;;) { // цикл по подключения socklen_t clilen = sizeof(serv_addr); int childpid, newsockfd; newsockfd = accept(sockfd, (struct sockaddr*)&serv_addr, &clilen); if (newsockfd < 0) // ожидать соединения err_dump("server: accept error"); if ((childpid = fork()) < 0) err_dump("server: fork error"); else if (childpid == 0) { // обрабатывать в дочернем процессе close(sockfd); // закрыть копию прослушивающего сокета str_echo(newsockfd); // ретранслировать полученные данные exit(0); // завершить дочерний процесс } close(newsockfd); // в роительском процессе } } EOF cat > unixdgcli.c << "EOF" /* Example of client using UNIX domain datagram protocol. */ #include "unix.h" int main(int argc, char* argv[]) { int sockfd, clilen, servlen; char *mktemp(); struct sockaddr_un cli_addr, serv_addr; /* Fill in the structure "serv_addr" with the address * of the server that we want to send to. */ bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, UNIXDG_PATH); servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path); /* Open a socket (a UNIX domain datagram socket). */ if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) err_dump("client: can't open datagram socket"); /* Bind a local address for us. * In the UNIX domain we have to choose our own name (that * should be unique). We'll use mktemp() to create a unique * pathname, based on our process id. */ bzero((char*)&cli_addr, sizeof(cli_addr)); /* zero out */ cli_addr.sun_family = AF_UNIX; strcpy(cli_addr.sun_path, UNIXDG_TMP); mktemp(cli_addr.sun_path); clilen = sizeof(cli_addr.sun_family) + strlen(cli_addr.sun_path); if (bind(sockfd, (struct sockaddr*)&cli_addr, clilen) < 0) err_dump("client: can't bind local address"); dg_cli(stdin, sockfd, (struct sockaddr*)&serv_addr, servlen); close(sockfd); unlink(cli_addr.sun_path); exit(0); } EOF cat > unixdgserv.c << "EOF" /* Example of server using UNIX domain datagram protocol. */ #include "unix.h" int main(int argc, char* argv[]) { int sockfd, servlen; struct sockaddr_un serv_addr, cli_addr; /* Open a socket (a UNIX domain datagram socket). */ if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) err_dump("server: can't open datagram socket"); /* Bind our local address so that the client can send to us. */ unlink(UNIXDG_PATH); /* in case it was left from last time */ bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, UNIXDG_PATH); servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path); if (bind(sockfd, (struct sockaddr*)&serv_addr, servlen) < 0) err_dump("server: can't bind local address"); dg_echo(sockfd, (struct sockaddr*)&cli_addr, sizeof(cli_addr)); } EOF cat > unix.h << "EOF" /* * Definitions for UNIX domain stream and datagram client/server programs. */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include "common.h" #define UNIXSTR_PATH "./s.unixstr" #define UNIXDG_PATH "./s.unixdg" #define UNIXDG_TMP "/tmp/dg.XXXXXX" char *pname; EOF cat > unixstrcli.c << "EOF" /* Example of client using UNIX domain stream protocol. */ #include "unix.h" int main(int argc, char* argv[]) { int sockfd, servlen; struct sockaddr_un serv_addr; /* Fill in the structure "serv_addr" with the address * of the server that we want to send to. */ bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, UNIXSTR_PATH); servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family); /* Open a socket (an UNIX domain stream socket). */ if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) err_sys("client: can't open stream socket"); /* Connect to the server. */ if (connect(sockfd, (struct sockaddr*)&serv_addr, servlen) < 0) err_sys("client: can't connect to server"); str_cli(stdin, sockfd); /* do it all */ close(sockfd); exit(0); } EOF cat > unixstrserv.c << "EOF" /* Example of server using UNIX domain stream protocol. */ #include "unix.h" int main(int argc, char* argv[]) { int sockfd, newsockfd, childpid, servlen; struct sockaddr_un cli_addr, serv_addr; /* Open a socket (a UNIX domain stream socket). */ if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) err_dump("server: can't open stream socket"); /* Bind our local address so that the client can send to us. */ bzero((char*)&serv_addr, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, UNIXSTR_PATH); servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family); if (bind(sockfd, (struct sockaddr*)&serv_addr, servlen) < 0) err_dump("server: can't bind local address"); listen(sockfd, 5); for (; ;) { /* Wait for a connection from a client process. * This is an example of a concurrent server. */ socklen_t clilen = sizeof(cli_addr); newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen); if (newsockfd < 0) err_dump("server: accept error"); if ((childpid = fork()) < 0) err_dump("server: fork error"); else if (childpid == 0) { /* child process */ close(sockfd); /* close original socket */ str_echo(newsockfd); /* process the request */ exit(0); } close(newsockfd); /* parent process */ } } EOF cat > writen.c << "EOF" #include "common.h" /* Write "n" bytes to a descriptor */ ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; nleft = n; while (nleft > 0) { if ((nwritten = write(fd, vptr, nleft)) <= 0) { if (errno == EINTR) nwritten = 0; /* and call write() again */ else return(-1); /* error */ } nleft -= nwritten; vptr += nwritten; } return(n); } EOF Для компиляции используй: make Использование: ./udpserv ./udpcli ./tcpserv ./tcpcli ./unixdgserv ./unixdgcli ./unixstrserv ./unixstrcli ./sockopt |
Драйверы и сетевые устройства в ядре Linux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Linux - операционная система с монолитным ядром. !!! Альтернативой системы с монолитным ядром является микро ядерные операционные системы (которых создано очень мало) Проблемы у монолитного ядра, это расширения функциональности (Решёное погрузкой модулей) Такие модули загружаясь связываются с API ядра и его структурами данных по абсолютным адресам размещения. Адреса всех модулей можно посмотреть в псевдо файле /proc/kallsyms и их кол-во может достигать десятков тысяч (с каждой новой версией ядра становится все больше) cat /proc/kallsyms Подсчитать количество: cat /proc/kallsyms | wc -l Пример что значит строки в выводе "cat /proc/kallsyms | head" : cat /proc/kallsyms | head -n1 ffff800008000000 T _text ffff800008000000 - это абсолютный адрес для вызова этой функции ядра T - сегмент текста (т.е. кода) _text - внешнее имя драйвера (модуля) |
ОС с микро ядерной архитектурой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Операционные системы с микроядерной архитектурой включают: Minix — учебная операционная система, использующая микроядро. Стала основой для создания концепции микроядерных систем. QNX — коммерческая реальная ОС с микроядром, широко используемая в автомобильной и встраиваемой электронике. L4 — семейство микроядер, которое используется в исследовательских и промышленных системах, таких как системы безопасности и встраиваемые системы. Есть несколько версий L4, например, L4Ka::Pistachio, Fiasco. GNU Hurd — проект свободной операционной системы, использующий микроядро Mach. Hurd разрабатывался как часть проекта GNU для замены Unix-подобных систем. Mach — изначально разработанное микроядро, которое повлияло на разработку других микроядер. Использовалось как основа для NeXTSTEP и позднее стало частью macOS в виде гибридного ядра XNU. Integrity — реальная ОС с микроядром, используемая в авиационной и медицинской технике. Genode — платформа, основанная на микроядре, ориентированная на безопасность и высокую модульность встраиваемых систем. |
Сборка своего модуля ядра hello_printk.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
Обратите внимание что данный Makefile создан с использованием макросов разработчиками ядра и он будет повторятся с минимальными изменениями для любых модулей ядра. cat > Makefile << "EOF" CONFIG_MODULE_SIG=n CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) DEST = /lib/modules/$(CURRENT)/misc TARGET = hello_printk obj-m := $(TARGET).o all: default clean #all: default default: $(MAKE) -C $(KDIR) M=$(PWD) modules install: cp -v $(TARGET).ko $(DEST) # /sbin/depmod -v | grep $(TARGET) /sbin/depmod /sbin/insmod $(TARGET).ko /sbin/lsmod | grep $(TARGET) uninstall: /sbin/rmmod $(TARGET) rm -v $(DEST)/$(TARGET).ko /sbin/depmod clean: @rm -f *.o .*.cmd .*.flags *.mod.c *.order @rm -f .*.*.cmd *.symvers *~ *.*~ @rm -fR .tmp* *.mod @rm -rf .tmp_versions disclean: clean @rm -f *.ko EOF # Исправить восемь пробелов на TAB sed -i 's/ /\t\t/g' Makefile cat > hello_printk.c << "EOF" #include <linux/module.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("LALA <mail@gmail.com>>"); static int __init hello_init(void) { printk("Hello, world!\n"); return 0; } static void __exit hello_exit(void) { printk("Goodbye, world!\n"); } module_init(hello_init); module_exit(hello_exit); EOF Для сборки используем: make !!!Обратите внимание что начиная с версии ядра 2.6 имя модуля ядра изменилось на ".o" на ".ko" Формат файла модуля является обычный объектный файл ELF, но дополненный в таблице внешних имен дополнительными именами (__mod_author5, __mod_license4, __mod_srcversion23, __module_depends, __mod_vermagic5 и прочими) Узнать информацию о собранном модуле можно командой modinfo: file hello_print.ko modinfo ./hello_print.ko Загрузить: !!! insmod - не проверяет зависимости, по этому ее использование опасно modprobe ./hello_print.ko Для выгрузки: rmmod hello_print |
Модули ядра, точки входа и завершения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
Любой модуль должен иметь объявление функции входа(инициализации) модуля и его завершения (не обязательно, может и отсутствовать). Функция инициализации будет вызываться после проверки и соблюдения всех условий достаточных для инициализации. Выполнение команды insmod для модуля. Функция инициализации имеет прототип и объявляется именно как функция инициализации макросом module_init(): static int __int hello_int(void) { ... } module_init(hello_init); Функция завершения будет вызывается при выполнение команды rmmod функция завершения имеет прототип module_exit(): static void __exit hello_exit(void){ ... } module_exit(hello_exit); Обратите внимание что функция завершения по своему прототипу не имеет возвращаемого значения и поэтому не может сообщить о невозможности каких=либо действий, когда уже начала выполнятся. Идея состоит в том что система сама проверит возможность завершения , и если это не возможно то просто не выполнит эту функцию. Существует еще один не документированный способ описания этих функций: а именно init_module() и clean_module() выглядят они так: int init_module(void) { ... } void cleanup_module(void) { ... } при такой записи необходимость в макросах module_int() и module_xit отпадает и задействовать квалификатор static с такими записями нельзя! такая запись ухудшает читаемость кода |
Модули ядра, вывод диагностики модуля:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Для диагностики модуля используется функция printk() По функционалу похож на функцию printf() Сам вызов printk() и сопутствующие ему константы \ определения вы найдете в файле: /lib/modules/`uname -r`/build/include/linux/kernel.h asmlinkage int printk(const char * fmt, ...) #define KERN_EMERG "<0>" #define KERN_ALERT "<1>" #define KERN_CRIT "<2>" #define KERN_ERR "<3>" #define KERN_WARNING "<4>" #define KERN_NOTICE "<5>" #define KERN_INFO "<6>" #define KERN_DEBUG "<7>" зависимости модулей прописаны в файле (информацию о них modprobe черпает из него) cat /lib/modules/`uname -r`/modules.dep Если modprobe получает универсальный идентификатор, то она сначала пытается найти его в файлах /etc/modprobe.conf (утарело) и в каталоге /etc/modprobe.d/*.conf !!! inmod никак не проверяет зависимости, если обнаруживает не разрешенные имена, то завершает работу аварийно. depmod - показывает зависимости |
Параметры загрузки модуля
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
Из командной строки передаются через массив argv[] Для каждого параметра определяется переменная-параметр, далее эти переменные используются в макросе module_param(). Подобный макрос следует записать для каждого предусмотренного параметра, и он должен последовательно определить: 1) имя параметра и переменной 2) тип значения этой переменной 3) права доступа к параметру (отображаемое как путевое имя в системе псевдо каталог /sys) Значения параметрам могут быть установлены во время загрузки модуля через insmod и modprobe modprobe также может прочитать файл /etc/modprobe.conf Обработка входных параметров модуля обеспечивается макросами описанными в <linux/moduleparam.h> И там множество! Но чаще всего вы встретите module_param() и module_param_array() Пример кода: cat > mod_params.c << "EOF" #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/string.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("OLA laA <mail@gmail.com>>"); static int iparam = 0; // целочисленный параметр module_param(iparam, int, 0); static int k = 0; // имена параметра и переменной различаются module_param_named(nparam, k, int, 0); static bool bparam = true; // логический инверсный парамер module_param(bparam, invbool, 0); static char* sparam; // строчный параметр module_param(sparam, charp, 0); #define FIXLEN 5 static char s[FIXLEN] = ""; // имена параметра и переменной различаются module_param_string(cparam, s, sizeof(s), 0); // копируемая строка static int aparam[] = { 0, 0, 0, 0, 0 }; // параметр - целочисленный массив static int arnum = sizeof(aparam)/ sizeof(aparam[0]); module_param_array(aparam, int, &arnum, S_IRUGO | S_IWUSR); static int __init mod_init(void) { int j; char msg[40] = ""; printk("========================================\n"); printk("iparam = %d\n", iparam); printk("nparam = %d\n", k); printk("bparam = %d\n", bparam); printk("sparam = %s\n", sparam); printk("cparam = %s {%ld}\n", s, (long)strlen(s)); sprintf(msg, "aparam [ %d ] = ", arnum ); for(j = 0; j < arnum; j++) sprintf(msg + strlen(msg), " %d ", aparam[j]); printk("%s\n", msg ); printk("========================================\n"); return -1; } module_init(mod_init); EOF cat > Makefile << "EOF" CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) TARGET = mod_params obj-m := $(TARGET).o all: default clean default: $(TARGET).c $(MAKE) -C $(KDIR) M=$(PWD) modules clean: @rm -f *.o .*.cmd .*.flags *.mod.c *.order @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.* @rm -fR .tmp* *.mod @rm -rf .tmp_versions disclean: clean @rm -f $(TARGET).ko EOF # Исправить восемь пробелов на TAB sed -i 's/ /\t\t/g' Makefile Примеры использования: insmod mod_params.ko insmod ./mod_params.ko iparam=3 sparam=zxcv aparam=5,4,3 insmod ./mod_params.ko iparam=3 sparam=zxcv aparam=5,4,3,2,1,0 insmod ./mod_params.ko iparam=qwerty insmod ./mod_params.ko iparam=3 nparam=4 sparam=str1 cparam=str2 aparam=5,4,3 insmod mod_params.ko sparam='new' insmod mod_params.ko iparam=3 nparam=4 bparam=1 sparam=str1 cparam=str2 aparam=5,4,3 Ошибка? echo $? Если последняя команда выполнилась и 0 нет ошибки, если > 0 есть ошибка Ну и вывод смотрим с помощью dmesg dmesg dmesg -w dmesg | tail -n9 |
Модуль ядра, подсчет ссылок использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Одно из важных понятий модуля. При загрузки модуля начальное значение счетчика ссылок нулевое. При загрузке любого следующего модуля, который использует(импортирует) имена, экспортируемые этим модулем, счетчик ссылок такого модуля инкрементируется. !!! Модуль, счетчик ссылок использования которого ненулевой, не может быть выгружен командой rmmod. Некорректное обращение к несуществующему модулю гарантирует крах всей системы. Пример: lsmod | grep i2c i2c_algo_bit 20480 1 i915 i2c_i801 45056 0 i2c_smbus 20480 1 i2c_i801 i2c_mux 16384 1 i2c_i801 i2c_dev 28672 0 Взглянем на: i2c_smbus 20480 1 i2c_i801 Значение ссылок равно 1 Далее i2c_i801 перечислены использующие его модули (модули которые на него ссылаются) До тех пор пока модуль i2c_i801 не будет удален из системы, модуль i2c_smbus будет невозможно удалить. функции вызова определены в файле <linux/module.h> int try_module _get(struct module *module) - увеличить счетчик ссылок для модуля (возвращает признак успешности операции) void module_put(struct module *module) - уменьшает счетчик ссылок для модуля unsigned int module_refcount(struct module *mod) - возвратить значение счетчика ссылок для модуля В качестве параметра всех этих вызовов, как правило, передается константный указатель THIS_MODULE, так что вызовы выглядят примерно так: try_module_get(THIS_MODULE); !!! Возможность управлять значениями счетчика ссылок из собственного модуля есть, но делать это не рекомендуется. |
Структура данных сетевого стека:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
Сетевая реализация построена так, чтобы не зависеть от конкретики протоколов. Основной структурой данных является struct net_device, описывает сетевое устройство. Основной структурой обмениваемых данных между сетевыми устройствами являются "буферы сокетов" Определена в <linux/skbuff.h> struct sk_buff данные управления "head" данные пакета "data" Буферы сокетов всегда указывают в очереди( struct sk_queue_head ) посредством своих первых полей "next" и "prev" Пример полей структуры: typedef unsigned char *sk_buff data_t; struct sk_buff { stuckt sk_buff *next; struct sk_buff *prev; ... sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; ... unsigned char *head, *data; ... }; Структура вложенности заголовков сетевых уровней в точности соответствует структуре инкапсуляции сетевых протоколов внутри друг друга - это позволяет обрабатывающему слою получить доступ к информации, относящейся только нужному ему слою. Экземпляры данных типа struct sk_buff: ПОЛУЧАТЕЛЬ: Возникают при поступлении очередного сетевого пакета из внешней физической среды распространения данных. Об этом событии извещает прерывание IRQ, генерируемое сетевым адаптером. При этом создается экземпляр буфера сокета, заполняется данными из поступающего пакета и далее передается вверх от сетевого слоя к слою до приложения прикладного уровня, которое является получателем пакета. На этом экземпляр данных буфера сокета уничтожается. ОТПРАВИТЕЛЬ: Возникает в среде приложения прикладного уровня, которое является отправителем пакета данных. Пакет отправителя данных помещается в созданный буфер сокета, который начинает перемещается вниз от сетевого слоя к слою достижения канального уровня L2. На этом уровне осуществляется физическая передача данных пакета через сетевой адаптер в среду распространения. В случае успешного завершения передачи (подтверждается прерыванием, генерируем сетевым адаптером , часто по той же линии IRQ, что и при приеме пакета) буфер сокета уничтожается. При отсутствии подтверждения отправки(IRQ) обычно делается несколько повторных попыток, прежде чем принять решение об ошибке канала. |
Путь пакета сквозь стек протоколов / Прием: традиционный подход
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Традиционный подход состоит в том, что каждый приходящий сетевой пакет порождает аппаратное прерывание по линии IRQ адаптера, что и служит сигналом на прием очередного сетевого пакета и создание буфера сокета для его сохранения и обработки принятых данных. Порядок действий: 1. Читая конфигурационную область PCI-адаптера сети при инициализации модуля, определяем линию прерывания IRQ, которая будет обслуживать сетевой обмен: char irq; pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &byte); 2. При инициализации сетевого интерфейса для этой линии IRQ устанавливается обработчик прерывания my_interrupt(): reques_irq(int)irq, my_interrupt, IRQF_SHARED, "my_interrupt", &my_dev_id); 3. В обработчике прерывания по приему нового пакета в сеть - здесь нужен анализ причины по чтению некоего аппаратного флага) создается (или запрашивается из пула используемых) новый экземпляр буфера сокетов: static irqreturn_t my_interrupt(int irq, void *dev_id) { ... struct sk_buff *skb = kmalloc(sizeof(struct sk_buff), ...); // заполнение данных *skb чтением из портов сетевого адаптера netif_rx(skb); return IRQ_HANDLED; } Все эти действия выполняются не в самом обработчике верхней половины прерываний от сетевого адаптера, а в обработчике отложенного прерывания NET_RX_SOFTIRQ для этой линии. Последним действием является передача заполненного сокетного буфера вызову netifrx() (или netif_receive_skb()), который и запустит процесс движения его (буфера) по структуре сетевого стека (отметит отложенное программное прерывание NET_RX_SOFTIRQ для исполнения). |
Путь пакета сквозь стек протоколов / Прием: высокоскоростной интерфейс
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
Особенность сетевых интерфейсов в том, что их активность носит взрывной характер. В один момент времени может не быть трафика совсем(обмен ARP пакетами не в счёт), в другой его много. !!! Современные сетевые интерфейсы имеют скорости 10+ Гбит, и уже при более скромных скоростях традиционный подход становится нецелесообразным: Новые приходящие пакеты создают вложенные запросы IRQ несколько уровней при еще не обслуженном приеме текущего IRQ; Асинхронное обслуживание каждого IRQ в плотном потоке создает слишком большие накладные расходы. Поэтому был добавлен набор API для обработки таких плотных потоков пакетов, поступающих с высокоскоростных интерфейсов, который и получил название NAPI (New API). Идея состоит в том, чтобы прием пакетов осуществлять не методом прерывания, а методом программного опроса (poling) точнее комбинацией этих двух возможностей: При поступлении первого пакета "пачки" инициируется прерывание IRQ адаптера(все начинается как в традиционном методе); В обработчике прерывания запрещается поступление дальнейших запросов прерывания с этой линии IRQ по приему пакетов, IRQ с этой линии по отправке пакетов могут продолжать пропускать - т.е. установленный запрет происходит не программным запретом линии IRQ со стороны процессора, а записью управляющей информации в аппаратные регистры сетевого адаптера. Тесть сетевой адаптер это должен уметь делать. (все современные адаптеры умет это делать) После прекращения прерываний по приему обработчик переходит в режим циклического считывания и обработки принятых из сети пакетов, сетевой адаптер при этом накапливает поступающие пакеты во внутреннем кольцевом буфере приема, либо до определенного порогового числа считанных пакетов (10, 20, ...), называемого бюджетом функции поллинга. Считывание и обработка пакетов происходит обработчике прерываний в его отстроченной (нижней) части. Каждому принятому в опросе пакету генерируется сокетный буфер для продвижения его по стеку сетевых протоколов вверх После завершения цикла программного опроса по его результатам устанавливается состояния завершения: NAPI_STATE_DISABLE(если не осталось считанных пакетов в кольцевом буфере адаптера) или NAPI_STATE_SCHED(что говорит, что устройство адаптера должно продолжать опрашиваться, когда ядро в следующий раз перейдет к циклу опросов в отложенном обработчике прерываний) Если результатом является NAPI_STATE_DISABLE, то после завершения цикла программного опроса восстанавливается разрешение генерации прерываний по линии IRQ приема пакетов(записью в порты сетевого адаптера) 1. Реализатор обязан создать и зарегистрировать специфичную функцию для модуля функции опроса(poll-функцию), используя вызов (<netdevice.h>) static inline void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *,int), int weight); dev - сетевой интерфейс poll - функция программного опроса weight - относительный вес \ 10MIb,100Mib = 16 \ 10Gib и 100Gib = 64 napi - указатель на специальную структуру, со значениями state = NAPI_STATE_DISABLE \ state = NAPI_STATE_SCHED struct napi_struct { struct list_head poll_list; unsingned long state; int weight; int (*poll) (struct napi_struct*, int); }; 2. Зарегистрированная функция программного опроса ( полностью зависимая от задачи и реализуемая в коде модуля имеет подобный вид; static int my_card_poll(struct napi_struct *napi, int budget) { int work_done; // число реально обработанных в цикле опроса сетевых пакетов work_done = my_card_input(budget, ...); // реализационно специфический прием пакетов if (work_done < budget ) { netif_rx_complete(netdev, napi); my_card_enable_irq(...); // разрешить IRQ приема } retun work_done; } my_card_input() - пытается аппаратно считать budget сетевых пакетов Для каждого считанного сетевого пакета создается сокетный буфер и вызывает netif_receive_skb() если кольцевой буфер исчерпается ранее budget то будет вызвана netif_rx_complete() что отменит отложенное програмное прерывание NET_RX_SOFTIRQ 3. Обработчик аппаратного прерывания линии IRQ сетевого адаптера: static irqretur_t my_interrupt(int irq, void *dev_id) { struct net_debice *netdev = dev_id; if (likely(netif_rx_schedule_prep(netdev, ...))) { my_card_disable_irq(...); // запретить IRQ приема __netif_rx_schedule(netdev, ...); } return IRQ_HANDLED; } Ядро должно быть уведомлено что новая порция сетевых пакетов готова для обработки. Для этого: вызов netif_rx_schedule_prep() устанавливает состояние NAPI_STATE_SCHED если он успешен, то вызовом __netif_rx_schedule() устройство помещается в список программного опроса в цикле обработки отложенного программного прерывания NET_RQ_SOFTIRQ Опрос не должен потреблять более одного системного тика(глобальная переменная jiffiels), иначе это будет искажать диспетчеризацию потоков ядра Бюджет не должен быть больше глобального установленного ограничения cat /proc/sys/net/core/netdev_budget |
Передача пакета:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
ndo (Net Device Operation) При инициализации сетевого интерфейса создается таблица операций сетевого интерфейса, одно из полей которой - - ndo_start_xmit - определяет функцию передачи пакета в сеть: struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = stub_start_xmit, }; При вызове stub_start_xmit должна обеспечить аппаратную передачу полученного сокета в сеть, после чего уничтожает(возвращает в пул) буфер сокета: static int stab_start_xmit(struct sk)buff *skb, struct net_device *dev) { // ... апаратное обслуживание передачи dev _kfree_skb(skb); return 0; } Реально чаще уничтожение отправлено буфера будет происходить не при инициализации операции, а при ее успешном завершении, что отслеживается той же IRQ, что и прием пакетов из сети. Часто задаваемый вопрос: а где же в этом процессе место (код), где создается информация для буферов?, где потребляется информация из принимаемых сокетных буферов? Ответ прост: Интерфейс из этого прикладного уровня в стек протоколов ядра обеспечивается известным POSIX API сокетов прикладного уровня. |
Драйверы: сетевой интерфейс
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 |
Задача сетевого интерфейса - быть местом в котором: Создаются экземпляры структуры "struct sk_buff" по каждому принятому из интерфейса пакету (здесь нужно принимать во внимание возможность сегментации IP-пакетов), далее созданный экземпляр структуры продвигается по стеку протоколов вверх до получателя пользовательского пространства, где он и уничтожается. Исходящие экземпляры структуры "struct sk_buff", порожденные где-то на верхних уровнях протоколов пользовательского пространства, должны отправляться (чаще всего каким-то аппаратным механизмом), а сами экземпляры структуры после этого - уничтожатся. Пример кода: cat > network.c << "EOF" /* * (c) Copyright Jerry Cooperstein, 2009 * (c) Copyright Oleg Tsiliuric, 2011 */ #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> static struct net_device *dev; static int my_open(struct net_device *dev) { printk(KERN_INFO "! Hit: my_open(%s)\n", dev->name); /* start up the transmission queue */ netif_start_queue(dev); return 0; } static int my_close(struct net_device *dev) { printk(KERN_INFO "! Hit: my_close(%s)\n", dev->name); /* shutdown the transmission queue */ netif_stop_queue(dev); return 0; } /* Note this method is only needed on some; without it module will fail upon removal or use. At any rate there is a memory leak whenever you try to send a packet through in any case*/ static int stub_start_xmit(struct sk_buff *skb, struct net_device *dev) { dev_kfree_skb(skb); return 0; } static struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = stub_start_xmit, }; static void my_setup(struct net_device *dev) { /* Fill in the MAC address with a phoney */ char *pa = (char*)dev->dev_addr; for(int j = 0; j < ETH_ALEN; j++) *pa++ = (char)j; ether_setup(dev); dev->netdev_ops = &ndo; } static int __init my_init(void) { printk(KERN_INFO "! Loading stub network module:...."); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(0, "fict%d", my_setup); #else dev = alloc_netdev(0, "fict%d", NET_NAME_ENUM, my_setup); #endif if(register_netdev(dev)) { printk(KERN_INFO "! Failed to register\n"); free_netdev(dev); return -1; } printk(KERN_INFO "! Succeeded in loading %s!\n", dev_name(&dev->dev)); return 0; } static void __exit my_exit(void) { printk(KERN_INFO "! Unloading stub network module\n"); unregister_netdev(dev); free_netdev(dev); } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("Bill Shubert"); MODULE_AUTHOR("Jerry Cooperstein"); MODULE_AUTHOR("Tatsuo Kawasaki"); MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_DESCRIPTION("LDD:1.0 s_24/lab1_network.c"); MODULE_LICENSE("GPL v2"); EOF cat > Makefile << "EOF" CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) DEST = /lib/modules/$(CURRENT)/misc EXTRA_CFLAGS += -std=gnu99 TARGET1 = devices TARGET2 = network TARGET3 = transmit_simple TARGET4 = transmit TARGET5 = receive TARGET6 = mulnet obj-m := $(TARGET1).o $(TARGET2).o $(TARGET3).o \ $(TARGET4).o $(TARGET5).o $(TARGET6).o all: default clean default: $(MAKE) -C $(KDIR) M=$(PWD) modules install: cp -v $(TARGET).ko $(DEST) /sbin/depmod -v | grep $(TARGET) /sbin/insmod $(TARGET).ko /sbin/lsmod | grep $(TARGET) uninstall: /sbin/rmmod $(TARGET) rm -v $(DEST)/$(TARGET).ko /sbin/depmod clean: @rm -f *.o .*.cmd .*.flags *.mod.c *.order @rm -f .*.*.cmd *.symvers *~ *.*~ @rm -fR .tmp* .*.d .*.cmd *.mod @rm -rf .tmp_versions disclean: clean @rm -f *.ko EOF # Исправить восемь пробелов на TAB sed -i 's/ /\t\t/g' Makefile cat > devices.c << "EOF" #include <linux/module.h> #include <linux/init.h> #include <linux/netdevice.h> static int __init my_init(void) { struct net_device *dev; printk(KERN_INFO "Hello: module loaded at 0x%px\n", my_init); dev = first_net_device(&init_net); printk(KERN_INFO "Hello: dev_base address=0x%px\n", dev); while (dev) { char smac[ETH_ALEN*3]; struct rtnl_link_stats64 stat = {}; for(int j = 0; j < ETH_ALEN; j++) sprintf(smac + j * 3, "%02x%c", dev->dev_addr[j], ETH_ALEN - 1 != j ? ':' : '\0' ); if (dev->netdev_ops->ndo_get_stats64 != NULL) dev->netdev_ops->ndo_get_stats64(dev, &stat); printk(KERN_INFO "name=%-6s irq=%-3d MAC=%s " "rx_bytes=%-8llu tx_bytes=%-8llu \n", dev->name, dev->irq, smac, stat.rx_bytes, stat.tx_bytes); dev = next_net_device(dev); } return -1; } module_init(my_init); MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_LICENSE("GPL v2"); EOF cat > transmit_simple.c << "EOF" /* * (c) Copyright Jerry Cooperstein, 2009 * (c) Copyright Oleg Tsiliuric, 2011-2022 */ /* * Building a Transmitting Network Driver (simple solution) * * Extend your stub network device driver to include a transmission * function, which means supplying a method for * dev->hard_start_xmit(). * * While you are at it, you may want to add other entry points to see * how you may exercise them. * * Once again, you should be able to exercise it with: * * insmod transmit_simple.ko * ifconfig mynet0 up 192.168.3.197 * ping -I mynet0 localhost * or * ping -bI mynet0 192.168.3 * * Make sure your chosen address is not being used by anything else. */ #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/netdevice.h> static int debug = 0; // уровень отладочного вывода module_param(debug, int, 0); static struct net_device *dev; static int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netdev_queue *pnq = skb_get_tx_queue(dev, skb); //static inline struct netdev_queue *skb_get_tx_queue(const struct net_device *dev, // const struct sk_buff *skb) pnq->trans_start = jiffies; if(debug > 0) printk(KERN_INFO "! my_hard_start_xmit(%s) - sending packet :\n", dev->name); if(debug > 1) { char line[3 * 16 + 1]; //, *plin = (char*)&line; /* print out 16 bytes per line */ for(int i = 0; i < skb->len; ++i) { int pos = i % 16; if((i > 0) && (i & 0xf) == 0) printk(KERN_INFO "! %s\n", line); sprintf((char*)line + pos * 3, "%02x ", skb->data[i]); } printk(KERN_INFO "\n"); } dev_kfree_skb(skb); return 0; } static int my_open(struct net_device *dev) { printk(KERN_INFO "! Hit: my_open(%s)\n", dev->name); /* start up the transmission queue */ netif_start_queue(dev); return 0; } static int my_close(struct net_device *dev) { printk(KERN_INFO "! Hit: my_close(%s)\n", dev->name); /* shutdown the transmission queue */ netif_stop_queue(dev); return 0; } static struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = my_hard_start_xmit, }; static void my_setup(struct net_device *dev ) { /* Fill in the MAC address with a phoney */ char *pa = (char*)dev->dev_addr; for(int j = 0; j < ETH_ALEN; j++) *pa++ = (char)j; ether_setup(dev); dev->netdev_ops = &ndo; dev->flags |= IFF_NOARP; } static int __init my_init(void) { printk(KERN_INFO "! Loading transmitting network module:...."); printk(KERN_INFO "! debug level = %d", (int)debug); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(0, "mynet%d", my_setup); #else dev = alloc_netdev(0, "mynet%d", NET_NAME_ENUM, my_setup); #endif if(register_netdev(dev)) { printk(KERN_INFO "! Failed to register\n"); free_netdev(dev); return -1; } printk(KERN_INFO "! Succeeded in loading %s!\n", dev_name(&dev->dev)); return 0; } static void __exit my_exit(void) { printk(KERN_INFO "! Unloading transmitting network module\n"); unregister_netdev(dev); free_netdev(dev); } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("Bill Shubert"); MODULE_AUTHOR("Jerry Cooperstein"); // "LDD:1.0 s_26/lab1_transmit_simple.c" MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_DESCRIPTION(""); MODULE_LICENSE("GPL v2"); EOF cat > transmit.c << "EOF" /* * (c) Copyright Jerry Cooperstein, 2009 * (c) Copyright Oleg Tsiliuric, 2011 */ #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/netdevice.h> static struct net_device *dev; static struct net_device_stats *stats; static int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netdev_queue *pnq = skb_get_tx_queue(dev, skb); pnq->trans_start = jiffies; printk(KERN_INFO "! my_hard_start_xmit(%s) - Sending packet :\n", dev->name); // printk(KERN_INFO "! Sending packet :\n"); for(int i = 0; i < skb->len; ++i) { /* print out 16 bytes per line */ char line[3 * 16 + 1]; int pos = i % 16; if((i > 0) && (i & 0xf) == 0) printk(KERN_INFO "! %s\n", line); sprintf((char*)line + pos * 3, "%02x ", skb->data[i]); } printk(KERN_INFO "\n"); dev_kfree_skb(skb); ++stats->tx_packets; return 0; } static int my_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { printk(KERN_INFO "! my_do_ioctl(%s)\n", dev->name); return -1; } static struct net_device_stats *my_get_stats(struct net_device *dev) { printk(KERN_INFO "! my_get_stats(%s)\n", dev->name); return stats; } /* This is where ifconfig comes down and tells us who we are, etc. * We can just ignore this. */ static int my_config(struct net_device *dev, struct ifmap *map) { printk(KERN_INFO "! my_config(%s)\n", dev->name); if(dev->flags & IFF_UP) return -EBUSY; return 0; } static int my_change_mtu(struct net_device *dev, int new_mtu) { printk(KERN_INFO "! my_change_mtu(%s)\n", dev->name); return -1; } static int my_open(struct net_device *dev) { printk(KERN_INFO "! Hit: my_open(%s)\n", dev->name); /* start up the transmission queue */ netif_start_queue(dev); return 0; } static int my_close(struct net_device *dev) { printk(KERN_INFO "! Hit: my_close(%s)\n", dev->name); /* shutdown the transmission queue */ netif_stop_queue(dev); return 0; } static struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = my_hard_start_xmit, .ndo_do_ioctl = my_do_ioctl, .ndo_get_stats = my_get_stats, .ndo_set_config = my_config, .ndo_change_mtu = my_change_mtu, }; static void my_setup(struct net_device *dev) { /* Fill in the MAC address with a phoney */ char *pa = (char*)dev->dev_addr; for(int j = 0; j < ETH_ALEN; j++) *pa++ = (char)j; ether_setup(dev); dev->netdev_ops = &ndo; dev->flags |= IFF_NOARP; stats = &dev->stats; /* Just for laughs, let's claim that we've seen 50 collisions. */ stats->collisions = 50; } static int __init my_init(void) { printk(KERN_INFO "! Loading transmitting network module:...."); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(0, "mynet%d", my_setup); #else dev = alloc_netdev(0, "mynet%d", NET_NAME_ENUM, my_setup); #endif if(register_netdev(dev)) { printk(KERN_INFO "! Failed to register\n"); free_netdev(dev); return -1; } printk(KERN_INFO "! Succeeded in loading %s!\n", dev_name(&dev->dev)); return 0; } static void __exit my_exit(void) { printk(KERN_INFO "! Unloading transmitting network module\n"); unregister_netdev(dev); free_netdev(dev); } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("Bill Shubert"); MODULE_AUTHOR("Jerry Cooperstein"); // LDD:1.0 s_26/lab1_transmit.c MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_DESCRIPTION(""); MODULE_LICENSE("GPL v2"); EOF cat > receive.c << "EOF" /* * (c) Copyright Jerry Cooperstein, 2009 * (c) Copyright Oleg Tsiliuric, 2011-2022 * * Adding Reception * * Extend your transmitting device driver to include a reception * function. * * You can do a loopback method in which any packet sent out is * received. * * Be careful not to create memory leaks! * */ #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/netdevice.h> static struct net_device *dev; static struct net_device_stats *stats; static void my_rx(struct sk_buff *skb, struct net_device *dev) { /* just a loopback, already has the skb */ printk(KERN_INFO "! I'm receiving a packet\n"); ++stats->rx_packets; netif_rx(skb); } static int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netdev_queue *pnq; printk(KERN_INFO "! my_hard_start_xmit(%s)\n", dev->name); pnq = skb_get_tx_queue(dev, skb); pnq->trans_start = jiffies; printk(KERN_INFO "! Sending packet :\n"); /* print out 16 bytes per line */ for(int i = 0; i < skb->len; ++i) { if((i & 0xf) == 0) printk(KERN_INFO "\n "); printk(KERN_INFO "%02x ", skb->data[i]); } printk(KERN_INFO "\n"); ++stats->tx_packets; /* loopback it */ my_rx(skb, dev); return 0; } static int my_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { printk(KERN_INFO "! my_do_ioctl(%s)\n", dev->name); return -1; } static struct net_device_stats *my_get_stats(struct net_device *dev) { printk(KERN_INFO "! my_get_stats(%s)\n", dev->name); return stats; } /* * This is where ifconfig comes down and tells us who we are, etc. * We can just ignore this. */ static int my_config(struct net_device *dev, struct ifmap *map) { printk(KERN_INFO "! my_config(%s)\n", dev->name); if(dev->flags & IFF_UP) return -EBUSY; return 0; } static int my_change_mtu(struct net_device *dev, int new_mtu) { printk(KERN_INFO "! my_change_mtu(%s)\n", dev->name); return -1; } static int my_open(struct net_device *dev) { printk(KERN_INFO "! Hit: my_open(%s)\n", dev->name); /* start up the transmission queue */ netif_start_queue(dev); return 0; } static int my_close(struct net_device *dev) { printk(KERN_INFO "! Hit: my_close(%s)\n", dev->name); /* shutdown the transmission queue */ netif_stop_queue(dev); return 0; } static struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = my_hard_start_xmit, .ndo_do_ioctl = my_do_ioctl, .ndo_get_stats = my_get_stats, .ndo_set_config = my_config, .ndo_change_mtu = my_change_mtu, }; static void my_setup(struct net_device *dev) { /* Fill in the MAC address with a phoney */ char *pa = (char*)dev->dev_addr; for(int j = 0; j < ETH_ALEN; j++) *pa++ = (char)j; ether_setup(dev); dev->netdev_ops = &ndo; dev->flags |= IFF_NOARP; stats = &dev->stats; /* Just for laughs, let's claim that we've seen 50 collisions. */ stats->collisions = 50; } static int __init my_init(void) { printk(KERN_INFO "! Loading transmitting network module:...."); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(0, "mynet%d", my_setup); #else dev = alloc_netdev(0, "mynet%d", NET_NAME_ENUM, my_setup); #endif if(register_netdev(dev)) { printk(KERN_INFO "! Failed to register\n"); free_netdev(dev); return -1; } printk(KERN_INFO "! Succeeded in loading %s!\n\n", dev_name(&dev->dev)); return 0; } static void __exit my_exit(void) { printk(KERN_INFO "! Unloading transmitting network module\n\n"); unregister_netdev(dev); free_netdev(dev); } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("Bill Shubert"); MODULE_AUTHOR("Jerry Cooperstein"); // LDD:1.0 s_26/lab2_receive.c MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_DESCRIPTION(""); MODULE_LICENSE("GPL v2"); EOF cat > mulnet.c << "EOF" /* * (c) Copyright Jerry Cooperstein, 2009 * (c) Copyright Oleg Tsiliuric, 2011-2022 */ #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> static uint num = 1; // число создаваемых интерфейсов module_param(num, uint, 0); static char* title; // префикс имени интерфейсов module_param(title, charp, 0); static int digit = 1; // числовые суфиксы (по умолчанию) module_param(digit, int, 0); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) static int mode = 1; // режим нумерации интерфейсов module_param(mode, int, 0); #endif #define MAX_LINK 5 static struct net_device* adev[MAX_LINK] = {}; //{ NULL, NULL, NULL, NULL, NULL, NULL }; static int my_open(struct net_device *dev) { printk(KERN_INFO "! my_open(%s)\n", dev->name); /* start up the transmission queue */ netif_start_queue(dev); return 0; } static int my_close(struct net_device *dev) { printk(KERN_INFO "! my_close(%s)\n", dev->name); /* shutdown the transmission queue */ netif_stop_queue(dev); return 0; } /* Note this method is only needed on some; without it module will fail upon removal or use. At any rate there is a memory leak whenever you try to send a packet through in any case*/ static int stub_start_xmit(struct sk_buff *skb, struct net_device *dev) { dev_kfree_skb(skb); return 0; } static struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = stub_start_xmit, }; static int ipos; static void __init my_setup(struct net_device *dev) { /* Fill in the MAC address with a phoney */ char *pa = (char*)dev->dev_addr; for(int j = 0; j < ETH_ALEN; j++) *pa++ = (char)(ipos + j); ether_setup(dev); dev->netdev_ops = &ndo; } static int __init my_init(void) { char prefix[20]; // if(num > sizeof(adev) / sizeof(adev[0])) { if( num > MAX_LINK ) { printk(KERN_INFO "! link number error"); return -EINVAL; } // NET_NAME_UNKNOWN, NET_NAME_ENUM, NET_NAME_PREDICTABLE, NET_NAME_USER, NET_NAME_RENAMED #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) if(mode < 0 || mode > NET_NAME_RENAMED) { printk(KERN_INFO "! unknown name assign mode"); return -EINVAL; } #endif printk(KERN_INFO "! loading network module for %d links", num); sprintf(prefix, "%s%s", (NULL == title ? "fict" : title), "%d"); for(ipos = 0; ipos < num; ipos++) { if(!digit) sprintf(prefix, "%s%c", (NULL == title ? "fict" : title), 'a' + ipos); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) adev[ipos] = alloc_netdev(0, prefix, my_setup); #else adev[ipos] = alloc_netdev(0, prefix, NET_NAME_UNKNOWN + mode, my_setup); #endif if(register_netdev(adev[ipos])) { printk(KERN_INFO "! failed to register"); for(int j = ipos; j >= 0; j--) { if(j != ipos) unregister_netdev(adev[ipos]); free_netdev(adev[ipos]); } return -ELNRNG; } } printk(KERN_INFO "! succeeded in loading %d devices!", num); return 0; } static void __exit my_exit(void) { printk(KERN_INFO "! unloading network module"); for(int i = 0; i < num; i++) { unregister_netdev(adev[i]); free_netdev(adev[i]); } } module_init(my_init); module_exit(my_exit); MODULE_AUTHOR("Bill Shubert"); MODULE_AUTHOR("Jerry Cooperstein"); MODULE_AUTHOR("Tatsuo Kawasaki"); MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_DESCRIPTION("LDD:1.0 s_24/lab1_network.c"); MODULE_LICENSE("GPL v2"); EOF Пробуем собрать фиктивный адаптер make insmod ./network.ko dmesg | tail n4 ip link show dev fict0 Основная структура "struct net_device" описания сетевого интерфейса находится в файле <linux/netdevice.h> Оттуда же и берется MAC "00:01:02:03:04:05" (генерируется) |
Виртуальный сетевой интерфейс и пример кода
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
cat > Makefile << "EOF" CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) TARGET0 = virt TARGET1 = virt1 TARGET2 = virt2 obj-m := $(TARGET0).o $(TARGET1).o $(TARGET2).o all: default clean default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: @rm -f *.o .*.cmd .*.flags *.mod.c *.order @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.* @rm -fR .tmp* *.mod @rm -rf .tmp_versions disclean: clean @rm -f *.ko EOF # Исправить восемь пробелов на TAB sed -i 's/ /\t\t/g' Makefile cat > virt.c << "EOF" #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/moduleparam.h> #include <net/arp.h> #define ERR(...) printk(KERN_ERR "! "__VA_ARGS__) #define LOG(...) printk(KERN_INFO "! "__VA_ARGS__) static char* link = "eth0"; module_param(link, charp, 0); static char* ifname = "virt"; module_param(ifname, charp, 0); static struct net_device *child = NULL; struct priv { struct net_device_stats stats; struct net_device *parent; }; static rx_handler_result_t handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; if(child) { struct priv *priv = netdev_priv(child); priv->stats.rx_packets++; priv->stats.rx_bytes += skb->len; LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name); skb->dev = child; /* netif_receive_skb(skb); return RX_HANDLER_CONSUMED; */ return RX_HANDLER_ANOTHER; } return RX_HANDLER_PASS; } static int open(struct net_device *dev) { netif_start_queue(dev); LOG("%s: device opened", dev->name); return 0; } static int stop(struct net_device *dev) { netif_stop_queue(dev); LOG("%s: device closed", dev->name); return 0; } static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) { struct priv *priv = netdev_priv(dev); priv->stats.tx_packets++; priv->stats.tx_bytes += skb->len; if(priv->parent) { skb->dev = priv->parent; skb->priority = 1; dev_queue_xmit(skb); LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name); return 0; } return NETDEV_TX_OK; } static struct net_device_stats *get_stats(struct net_device *dev) { return &((struct priv*)netdev_priv(dev))->stats; } static struct net_device_ops crypto_net_device_ops = { .ndo_open = open, .ndo_stop = stop, .ndo_get_stats = get_stats, .ndo_start_xmit = start_xmit, }; // #define MAX_ADDR_LEN 32 <netdev.h> // #define ETH_ALEN 6 /* Octets in one ethernet addr */ <if_ether.h> static void setup(struct net_device *dev) { int j; ether_setup(dev); memset(netdev_priv(dev), 0, sizeof(struct priv)); dev->netdev_ops = &crypto_net_device_ops; for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney dev->dev_addr[j]= (char)j; } int __init init(void) { int err = 0; struct priv *priv; char ifstr[40]; sprintf(ifstr, "%s%s", ifname, "%d"); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) child = alloc_netdev(sizeof(struct priv), ifstr, setup); #else child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup); #endif if(child == NULL) { ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM; } priv = netdev_priv(child); priv->parent = __dev_get_by_name(&init_net, link); // parent interface if(!priv->parent) { ERR("%s: no such link: %s", THIS_MODULE->name, link); err = -ENODEV; goto err; } if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) { ERR("%s: illegal net type", THIS_MODULE->name); err = -EINVAL; goto err; } /* also, and clone its IP, MAC and other information */ memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN); memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN); if((err = dev_alloc_name(child, child->name))) { ERR("%s: allocate name, error %i", THIS_MODULE->name, err); err = -EIO; goto err; } register_netdev(child); rtnl_lock(); netdev_rx_handler_register(priv->parent, &handle_frame, NULL); rtnl_unlock(); LOG("module %s loaded", THIS_MODULE->name); LOG("%s: create link %s", THIS_MODULE->name, child->name); LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name); return 0; err: free_netdev(child); return err; } void __exit virt_exit(void) { struct priv *priv = netdev_priv(child); if(priv->parent) { rtnl_lock(); netdev_rx_handler_unregister(priv->parent); rtnl_unlock(); LOG("unregister rx handler for %s\n", priv->parent->name); } unregister_netdev(child); free_netdev(child); LOG("module %s unloaded", THIS_MODULE->name); } module_init(init); module_exit(virt_exit); MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_AUTHOR("Nikita Dorokhin"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("3.2"); EOF cat > virt1.c << "EOF" #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/moduleparam.h> #include <net/arp.h> #define ERR(...) printk(KERN_ERR "! "__VA_ARGS__) #define LOG(...) printk(KERN_INFO "! "__VA_ARGS__) static char* link = "eth0"; module_param(link, charp, 0); static char* ifname = "virt"; module_param(ifname, charp, 0); static struct net_device *child = NULL; static struct net_device_stats stats; struct priv { struct net_device *parent; }; static rx_handler_result_t handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; if(child) { stats.rx_packets++; stats.rx_bytes += skb->len; LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name); skb->dev = child; /* netif_receive_skb(skb); return RX_HANDLER_CONSUMED; */ return RX_HANDLER_ANOTHER; } return RX_HANDLER_PASS; } static int open(struct net_device *dev) { netif_start_queue(dev); LOG("%s: device opened", dev->name); return 0; } static int stop(struct net_device *dev) { netif_stop_queue(dev); LOG("%s: device closed", dev->name); return 0; } static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) { struct priv *priv = netdev_priv(dev); stats.tx_packets++; stats.tx_bytes += skb->len; if(priv->parent) { skb->dev = priv->parent; skb->priority = 1; dev_queue_xmit(skb); LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name); return 0; } return NETDEV_TX_OK; } static struct net_device_stats *get_stats(struct net_device *dev) { return &stats; } static struct net_device_ops crypto_net_device_ops = { .ndo_open = open, .ndo_stop = stop, .ndo_get_stats = get_stats, .ndo_start_xmit = start_xmit, }; // #define MAX_ADDR_LEN 32 <netdev.h> // #define ETH_ALEN 6 /* Octets in one ethernet addr */ <if_ether.h> static void setup(struct net_device *dev) { int j; ether_setup(dev); memset(netdev_priv(dev), 0, sizeof(struct priv)); dev->netdev_ops = &crypto_net_device_ops; for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney dev->dev_addr[j] = (char)j; } int __init init(void) { int err = 0; struct priv *priv; char ifstr[40]; sprintf(ifstr, "%s%s", ifname, "%d"); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) child = alloc_netdev(sizeof(struct priv), ifstr, setup); #else child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup); #endif if(child == NULL) { ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM; } priv = netdev_priv(child); priv->parent = __dev_get_by_name(&init_net, link); // parent interface if(!priv->parent) { ERR("%s: no such link: %s", THIS_MODULE->name, link); err = -ENODEV; goto err; } if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) { ERR("%s: illegal net type", THIS_MODULE->name); err = -EINVAL; goto err; } /* also, and clone its IP, MAC and other information */ memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN); memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN); if((err = dev_alloc_name(child, child->name))) { ERR("%s: allocate name, error %i", THIS_MODULE->name, err); err = -EIO; goto err; } register_netdev(child); rtnl_lock(); netdev_rx_handler_register(priv->parent, &handle_frame, NULL); rtnl_unlock(); LOG("module %s loaded", THIS_MODULE->name); LOG("%s: create link %s", THIS_MODULE->name, child->name); LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name); return 0; err: free_netdev(child); return err; } void __exit virt_exit(void) { struct priv *priv = netdev_priv(child); if(priv->parent) { rtnl_lock(); netdev_rx_handler_unregister(priv->parent); rtnl_unlock(); LOG("unregister rx handler for %s\n", priv->parent->name); } unregister_netdev(child); free_netdev(child); LOG("module %s unloaded", THIS_MODULE->name); } module_init(init); module_exit(virt_exit); MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_AUTHOR("Nikita Dorokhin"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("3.2"); EOF cat > virt2.c << "EOF" #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/moduleparam.h> #include <net/arp.h> #define ERR(...) printk(KERN_ERR "! "__VA_ARGS__) #define LOG(...) printk(KERN_INFO "! "__VA_ARGS__) static char* link = "eth0"; module_param(link, charp, 0); static char* ifname = "virt"; module_param(ifname, charp, 0); static struct net_device *child = NULL; struct priv { struct net_device *parent; }; static rx_handler_result_t handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; if(child) { child->stats.rx_packets++; child->stats.rx_bytes += skb->len; LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name); skb->dev = child; /* netif_receive_skb(skb); return RX_HANDLER_CONSUMED; */ return RX_HANDLER_ANOTHER; } return RX_HANDLER_PASS; } static int open(struct net_device *dev) { netif_start_queue(dev); LOG("%s: device opened", dev->name); return 0; } static int stop(struct net_device *dev) { netif_stop_queue(dev); LOG("%s: device closed", dev->name); return 0; } static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) { struct priv *priv = netdev_priv(dev); child->stats.tx_packets++; child->stats.tx_bytes += skb->len; if(priv->parent) { skb->dev = priv->parent; skb->priority = 1; dev_queue_xmit(skb); LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name); return 0; } return NETDEV_TX_OK; } static struct net_device_stats *get_stats(struct net_device *dev) { return &dev->stats; } static struct net_device_ops crypto_net_device_ops = { .ndo_open = open, .ndo_stop = stop, .ndo_get_stats = get_stats, .ndo_start_xmit = start_xmit, }; // #define MAX_ADDR_LEN 32 <netdev.h> // #define ETH_ALEN 6 /* Octets in one ethernet addr */ <if_ether.h> static void setup(struct net_device *dev) { int j; ether_setup(dev); memset(netdev_priv(dev), 0, sizeof(struct priv)); dev->netdev_ops = &crypto_net_device_ops; for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney dev->dev_addr[j] = (char)j; } int __init init(void) { int err = 0; struct priv *priv; char ifstr[40]; sprintf(ifstr, "%s%s", ifname, "%d"); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) child = alloc_netdev(sizeof(struct priv), ifstr, setup); #else child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup); #endif if(child == NULL) { ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM; } priv = netdev_priv(child); priv->parent = __dev_get_by_name(&init_net, link); // parent interface if(!priv->parent) { ERR("%s: no such link: %s", THIS_MODULE->name, link); err = -ENODEV; goto err; } if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) { ERR("%s: illegal net type", THIS_MODULE->name); err = -EINVAL; goto err; } /* also, and clone its IP, MAC and other information */ memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN); memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN); if((err = dev_alloc_name(child, child->name))) { ERR("%s: allocate name, error %i", THIS_MODULE->name, err); err = -EIO; goto err; } register_netdev(child); rtnl_lock(); netdev_rx_handler_register(priv->parent, &handle_frame, NULL); rtnl_unlock(); LOG("module %s loaded", THIS_MODULE->name); LOG("%s: create link %s", THIS_MODULE->name, child->name); LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name); return 0; err: free_netdev(child); return err; } void __exit virt_exit(void) { struct priv *priv = netdev_priv(child); if(priv->parent) { rtnl_lock(); netdev_rx_handler_unregister(priv->parent); rtnl_unlock(); LOG("unregister rx handler for %s\n", priv->parent->name); } unregister_netdev(child); free_netdev(child); LOG("module %s unloaded", THIS_MODULE->name); } module_init(init); module_exit(virt_exit); MODULE_AUTHOR("Oleg Tsiliuric"); MODULE_AUTHOR("Nikita Dorokhin"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("3.2"); EOF ЧДД? make insmod virt.ko link=p7p1 ifconfig virt0 192.168.50.2 ping 192.168.50.1 rmmod virt virt.1.ko link=p7p1 lsmod | head -n3 rmmod 'virt.1' |
Протокол сетевого уровня:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
На этом уровне (L2) обеспечивается обработка таких протоколов как IP/IPv4/IPv6/IPX/ICMP/RIP/OSPF/ARP или оригинальных пользовательских протоколов. Для установки обработчиков сетевого уровня предоставляются API сетевого уровня (<linux/netdevice.h>) Фактически в протокольных модулях мы должны добавить фильтр, через который проходят буферы сокетов из входящего потока интерфейса. (Исходящий поток реализуется проще) На обработку функции отбираются те буферы сокетов, которые удовлетворяют критериям, заложенным в структуре "struct packet_type" Примеры кода: cat > Makefile << "EOF" CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) DEST = /lib/modules/$(CURRENT)/misc EXTRA_CFLAGS += -std=gnu99 TARGET1 = net_proto TARGET2 = net_proto2 TARGET3 = net_protox TARGET4 = trn_proto obj-m := $(TARGET1).o #$(TARGET2).o $(TARGET3).o all: modules clean modules: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: @rm -f *.o .*.cmd .*.flags *.mod.c *.order *.d @rm -f .*.*.cmd *.symvers *~ *.*~ @rm -fR .tmp* .*.cmd .*.d @rm -rf .tmp_versions disclean: clean @rm -f *.ko EOF cat > net_proto.c << "EOF" #include "net_common.c" // int (*func) (struct sk_buff *, struct net_device *, // struct packet_type *, // struct net_device *); int test_pack_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *odev) { // printk(KERN_INFO "packet received with length: %u\n", skb->len); kfree_skb(skb); return skb->len; }; #define TEST_PROTO_ID 0x1234 static struct packet_type test_proto = { __constant_htons(ETH_P_ALL), // may be: __constant_htons(TEST_PROTO_ID), false, NULL, test_pack_rcv, (void*)1, NULL }; static int __init my_init(void) { dev_add_pack(&test_proto); LOG("module %s loaded\n", THIS_MODULE->name); return 0; } static void __exit my_exit(void) { dev_remove_pack(&test_proto); LOG("module %s unloaded\n", THIS_MODULE->name); } /* struct packet_type { // 5.4 __be16 type; // This is really htons(ether_type). bool ignore_outgoing; struct net_device *dev; // NULL is wildcarded here int (*func) (struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); void (*list_func) (struct list_head *, struct packet_type *, struct net_device *); bool (*id_match)(struct packet_type *ptype, struct sock *sk); void *af_packet_priv; struct list_head list; }; */ /* struct packet_type { __be16 type; // This is really htons(ether_type). struct net_device *dev; // NULL is wildcarded here (*func) ( struct sk_buff *, struct net_device *, struct packet_type *, struct net_device * ); struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features ); int (*gso_send_check)( struct sk_buff *skb ); struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb ); int (*gro_complete)( struct sk_buff *skb ); void *af_packet_priv; struct list_head list; }; */ EOF cat > net_proto2.c << "EOF" #include "net_common.c" static int debug = 0; module_param( debug, int, 0 ); static char* link = NULL; module_param( link, charp, 0 ); int test_pack_rcv_1( struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *odev ) { int s = atomic_read( &skb->users ); kfree_skb( skb ); if( debug > 0 ) LOG( "function #1 - %p => users: %d->%d\n", skb, s, atomic_read( &skb->users ) ); return skb->len; }; int test_pack_rcv_2( struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *odev ) { int s = atomic_read( &skb->users ); kfree_skb( skb ); if( debug > 0 ) LOG( "function #2 - %p => users: %d->%d\n", skb, s, atomic_read( &skb->users ) ); return skb->len; }; static struct packet_type test_proto1 = { __constant_htons( ETH_P_IP ), NULL, test_pack_rcv_1, (void*)1, NULL }, test_proto2 = { __constant_htons( ETH_P_IP ), NULL, test_pack_rcv_2, (void*)1, NULL }; static int __init my_init( void ) { if( link != NULL ) { struct net_device *dev = __dev_get_by_name( &init_net, link ); if( NULL == dev ) { ERR( "%s: illegal link", link ); return -EINVAL; } test_proto1.dev = test_proto2.dev = dev; } dev_add_pack( &test_proto1 ); dev_add_pack( &test_proto2 ); if( NULL == test_proto1.dev ) LOG( "module %s loaded for all links\n", THIS_MODULE->name ); else LOG( "module %s loaded for link %s\n", THIS_MODULE->name, link ); return 0; } static void __exit my_exit( void ) { if( test_proto2.dev != NULL ) dev_put( test_proto2.dev ); if( test_proto1.dev != NULL ) dev_put( test_proto1.dev ); dev_remove_pack( &test_proto2 ); dev_remove_pack( &test_proto1 ); LOG( "module %s unloaded\n", THIS_MODULE->name ); } /* struct packet_type { __be16 type; // This is really htons(ether_type). struct net_device *dev; // NULL is wildcarded here (*func) ( struct sk_buff *, struct net_device *, struct packet_type *, struct net_device * ); struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features ); int (*gso_send_check)( struct sk_buff *skb ); struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb ); int (*gro_complete)( struct sk_buff *skb ); void *af_packet_priv; struct list_head list; }; */ EOF cat > net_protox.c << "EOF" #include "net_common.c" int test_pack_rcv( struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *odev ) { LOG( "packet received with length: %u\n", skb->len ); kfree_skb( skb ); return skb->len; }; #define TEST_PROTO_ID 0x1234 static struct packet_type test_proto = { // __constant_htons( TEST_PROTO_ID ), __constant_htons( ETH_P_IP ), NULL, test_pack_rcv, (void*)1, NULL }; static int my_open( struct net_device *dev ) { LOG( "my_open(%s)\n", dev->name ); /* start up the transmission queue */ netif_start_queue( dev ); return 0; } static int my_close( struct net_device *dev ) { LOG( "my_close(%s)\n", dev->name ); /* shutdown the transmission queue */ netif_stop_queue( dev ); return 0; } static int stub_start_xmit( struct sk_buff *skb, struct net_device *dev ) { dev_kfree_skb( skb ); return 0; } static struct net_device_ops ndo = { .ndo_open = my_open, .ndo_stop = my_close, .ndo_start_xmit = stub_start_xmit, }; static void __init my_setup( struct net_device *dev ) { int j; for( j = 0; j < ETH_ALEN; ++j ) dev->dev_addr[ j ] = (char)j; // Fill the MAC address ether_setup( dev ); dev->netdev_ops = &ndo; } static struct net_device *dev; static int __init my_init( void ) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev( 0, "xxx%d", my_setup ); #else dev = alloc_netdev( 0, "xxx%d", NET_NAME_UNKNOWN, my_setup ); #endif if( register_netdev( dev ) ) { ERR( "failed to register\n" ); free_netdev( dev ); return -1; } test_proto.dev = dev; dev_add_pack( &test_proto ); LOG( "module loaded\n" ); return 0; } static void __exit my_exit( void ) { dev_remove_pack( &test_proto ); unregister_netdev( dev ); free_netdev( dev ); LOG( "module unloaded\n" ); } /* struct packet_type { __be16 type; // This is really htons(ether_type) struct net_device *dev; // NULL is wildcarded here (*func) ( struct sk_buff *, struct net_device *, struct packet_type *, struct net_device * ); struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features ); int (*gso_send_check)( struct sk_buff *skb ); struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb ); int (*gro_complete)( struct sk_buff *skb ); void *af_packet_priv; struct list_head list; }; */ EOF cat > trn_proto.c < "EOF" #include <net/protocol.h> #include "net_common.c" int test_proto_rcv( struct sk_buff *skb ) { printk( KERN_INFO "Packet received with length: %u\n", skb->len ); return skb->len; }; /* This is used to register protocols. struct net_protocol { int (*handler)( struct sk_buff *skb ); void (*err_handler)( struct sk_buff *skb, u32 info ); int (*gso_send_check)( struct sk_buff *skb ); struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features); struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb ); int (*gro_complete)( struct sk_buff *skb ); unsigned int no_policy:1, netns_ok:1; }; */ static struct net_protocol test_proto = { .handler = test_proto_rcv, .err_handler = 0, .no_policy = 0, }; #define PROTO IPPROTO_ICMP //#define PROTO IPPROTO_TCP //#define PROTO IPPROTO_RAW // #define PROTO IPPROTO_UDP static int __init my_init( void ) { int ret; if( ( ret = inet_add_protocol( &test_proto, PROTO ) ) < 0 ) { printk( KERN_INFO "proto init: can't add protocol\n"); return ret; }; printk( KERN_INFO "proto module loaded\n" ); return 0; } static void __exit my_exit( void ) { inet_del_protocol( &test_proto, PROTO ); printk( KERN_INFO "proto module unloaded\n" ); } EOF ЧТДСХ? insmod net_proto.ko insmod net_protox.ko insmod trn_proto.ko insmod net_proto2.ko link=p7p1 dmesg | tail -n30 | grep -v ^audit ls -R /sys/module/trn_proto rmmod trn_proto dmesg | tail -n60 | grep -v ^audit Очень большое число идентификаторов протоколов содержится в файле <linux/if_ether.h> |
Методика проверки драйвера на утечку памяти:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Утечка памяти может приводить к краху системы. 0. Запустить команду free и зафиксировать показания free 1. На тестируемом узле запустить: cat > client << "EOF" LIMIT=1000000 for ((a=1; a <= LIMIT ; a++)) do rm z.txt nc localhost 12345 > z.txt sleep 1 done EOF ./client 2. На стороннем доступном узле запустить: cat > server < "EOF" dd if=/dev/zero of=z.txt bs=1M count=1 LIMIT=1000000 for ((a=1; a <= LIMIT ; a++)) do cat z.txt | nc -l 12345 done EOF ./server 3. Выждать 10-20 минут За это время система не должна зависнуть. А результат команды free должен быть близок к результату из пункта 0. |
Драйвер virt-full
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
cat > Makefile << "EOF" CURRENT = $(shell uname -r) KDIR = /lib/modules/$(CURRENT)/build PWD = $(shell pwd) TARGET = virt obj-m := $(TARGET).o all: default clean default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: @rm -f *.o .*.cmd .*.flags *.mod.c *.order @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.* @rm -fR .tmp* *.mod @rm -rf .tmp_versions disclean: clean @rm -f $(TARGET).ko EOF cat > virt.c << "EOF" #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/inetdevice.h> #include <linux/moduleparam.h> #include <net/arp.h> #include <linux/ip.h> #define ERR(...) printk( KERN_ERR "! "__VA_ARGS__ ) #define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ ) #define DBG(...) if( debug != 0 ) printk( KERN_INFO "! "__VA_ARGS__ ) static char* link = "eth0"; module_param( link, charp, 0 ); static char* ifname = "virt"; module_param( ifname, charp, 0 ); static int debug = 0; module_param( debug, int, 0 ); static struct net_device *child = NULL; static struct net_device_stats stats; static u32 child_ip; struct priv { struct net_device *parent; }; static char* strIP( u32 addr ) { // диагностика IP в точечной нотации static char saddr[ MAX_ADDR_LEN ]; sprintf( saddr, "%d.%d.%d.%d", ( addr ) & 0xFF, ( addr >> 8 ) & 0xFF, ( addr >> 16 ) & 0xFF, ( addr >> 24 ) & 0xFF ); return saddr; } static char* strAR_IP( unsigned char addr[ 4 ] ) { static char saddr[ MAX_ADDR_LEN ]; sprintf( saddr, "%d.%0d.%d.%d", addr[ 0 ], addr[ 1 ], addr[ 2 ], addr[ 3 ] ); return saddr; } //<linux/if_arp.h> : struct arp_eth_body { unsigned char ar_sha[ ETH_ALEN ]; // sender hardware address unsigned char ar_sip[ 4 ]; // sender IP address unsigned char ar_tha[ ETH_ALEN ]; // target hardware address unsigned char ar_tip[ 4 ]; // target IP address }; static rx_handler_result_t handle_frame( struct sk_buff **pskb ) { struct sk_buff *skb = *pskb; if( skb->protocol == htons( ETH_P_IP ) ) { struct iphdr *ip = ip_hdr( skb ); char *addr = strIP( ip->daddr ); DBG( "rx: IP4 to IP=%s", addr ); if( ip->daddr != child_ip ) return RX_HANDLER_PASS; } else if( skb->protocol == htons( ETH_P_ARP ) ) { struct arphdr *arp = arp_hdr( skb ); struct arp_eth_body *body = (void*)arp + sizeof( struct arphdr ); int i, ip = child_ip; DBG( "rx: ARP for %s", strAR_IP( body->ar_tip ) ); for( i = 0; i < sizeof( body->ar_tip ); i++ ) { if( ( ip & 0xFF ) != body->ar_tip[ i ] ) break; ip = ip >> 8; } if( i < sizeof( body->ar_tip ) ) return RX_HANDLER_PASS; } else // не ARP и не IP4 return RX_HANDLER_PASS; stats.rx_packets++; stats.rx_bytes += skb->len; DBG( "rx: injecting frame from %s to %s", skb->dev->name, child->name ); skb->dev = child; return RX_HANDLER_ANOTHER; } static netdev_tx_t start_xmit( struct sk_buff *skb, struct net_device *dev ) { struct priv *priv = netdev_priv( dev ); stats.tx_packets++; stats.tx_bytes += skb->len; if( priv->parent ) { skb->dev = priv->parent; skb->priority = 1; dev_queue_xmit( skb ); LOG( "tx: injecting frame from %s to %s", dev->name, skb->dev->name ); return 0; } return NETDEV_TX_OK; } static int open( struct net_device *dev ) { struct in_device *in_dev = dev->ip_ptr; struct in_ifaddr *ifa = in_dev->ifa_list; /* IP ifaddr chain */ char sdebg[ 40 ] = ""; LOG( "%s: device opened", dev->name ); child_ip = ifa->ifa_address; sprintf( sdebg, "%s:", strIP( ifa->ifa_address ) ); strcat( sdebg, strIP( ifa->ifa_mask ) ); DBG( "%s: %s", dev->name, sdebg ); netif_start_queue( dev ); return 0; } static int stop( struct net_device *dev ) { LOG( "%s: device closed", dev->name ); netif_stop_queue( dev ); return 0; } static struct net_device_stats *get_stats( struct net_device *dev ) { return &stats; } static struct net_device_ops crypto_net_device_ops = { .ndo_open = open, .ndo_stop = stop, .ndo_get_stats = get_stats, .ndo_start_xmit = start_xmit, }; static void setup( struct net_device *dev ) { int j; ether_setup( dev ); memset( netdev_priv(dev), 0, sizeof( struct priv ) ); dev->netdev_ops = &crypto_net_device_ops; for( j = 0; j < ETH_ALEN; ++j ) // fill in the MAC address with a phoney dev->dev_addr[ j ] = (char)j; } int __init init( void ) { int err = 0; struct priv *priv; char ifstr[ 40 ]; sprintf( ifstr, "%s%s", ifname, "%d" ); #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) child = alloc_netdev( sizeof( struct priv ), ifstr, setup ); #else child = alloc_netdev( sizeof( struct priv ), ifstr, NET_NAME_UNKNOWN, setup ); #endif if( child == NULL ) { ERR( "%s: allocate error", THIS_MODULE->name ); return -ENOMEM; } priv = netdev_priv( child ); priv->parent = __dev_get_by_name( &init_net, link ); // parent interface if( !priv->parent ) { ERR( "%s: no such net: %s", THIS_MODULE->name, link ); err = -ENODEV; goto err; } if( priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK ) { ERR( "%s: illegal net type", THIS_MODULE->name ); err = -EINVAL; goto err; } /* also, and clone its IP, MAC and other information */ memcpy( child->dev_addr, priv->parent->dev_addr, ETH_ALEN ); memcpy( child->broadcast, priv->parent->broadcast, ETH_ALEN ); if( ( err = dev_alloc_name( child, child->name ) ) ) { ERR( "%s: allocate name, error %i", THIS_MODULE->name, err ); err = -EIO; goto err; } register_netdev( child ); rtnl_lock(); netdev_rx_handler_register( priv->parent, &handle_frame, NULL ); rtnl_unlock(); LOG( "module %s loaded", THIS_MODULE->name ); LOG( "%s: create link %s", THIS_MODULE->name, child->name ); LOG( "%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name ); return 0; err: free_netdev( child ); return err; } void __exit virt_exit( void ) { struct priv *priv = netdev_priv( child ); rtnl_lock(); netdev_rx_handler_unregister( priv->parent ); rtnl_unlock(); LOG( "%s: unregister rx handler for %s\n", THIS_MODULE->name, priv->parent->name ); unregister_netdev( child ); free_netdev( child ); LOG( "module %s unloaded", THIS_MODULE->name ); } module_init( init ); module_exit( virt_exit ); MODULE_AUTHOR( "Oleg Tsiliuric" ); MODULE_AUTHOR( "Nikita Dorokhin" ); MODULE_LICENSE( "GPL v2" ); MODULE_VERSION( "4.5" ); EOF ЧДТ? make insmod ./virt.ko link=p7p1 debug=1 dmesg | tail -n3 ifconfig virt0 192.168.50.2 dmesg | tail -n26 tcpdump -i virt0 dmesg | tail -n19 ping 192.168.50.1 dmesg | tail -n30 rmmod virt dmesg | tail -n4 ls -l *.ko make ls -l sudo insmod virt.ko sudo insmod virt.ko link=eno2 lsmod | head -n4 ping -I eno2 -c3 192.168.1.142 dmesg | tail -n8 dmesg | tail -n90 lsmod | head -n4 ip a s rmmod virt |
TOR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
Прокси/VPN TOR (The Omion Router) Свободное и открытое программное обеспечение для реализации второго (V2) и третьего (V3) поколения так называемой луковой маршрутизации: ваши данные - это сердцевина луковицы а их защита это слоя вокруг ПК <=> входная точка <=> опорная точка <=> выходная точка <=> веб-сайт Каждый пакет данных, передаваемый системой узлов Tor, через три разных узла, которые выбираются случайным образом в начале сеанса и могут быть изменены по ходу его прохождения. Перед отправкой пакет последовательно шифруется тремя ключами, сначала третий узел, второй, первый. Когда первый узел получает пакет, он расшифровывает верхний слой и так далее для остальных узлов. Установка tor для любых служб apt show tor apt install tor systemctl status tor pd -A | grep tor netstat -l -t | grep 9050 curl check-host.net/ip curl -x socks4://127.0.0.1:9050 check-host.net/ip whois 8.8.8.8 | grep -i country: Свой ресурс onion Для этого нужно отредактировать файл "/etc/tor/torrc" vim /etc/tor/torrc ------------------ HiddenServiceDir /var/lib/tor/onion.service HiddenServiceProt 80 127.0.0.1:80 HiddenServicePort 443 127.0.0.1:443 #HiddenServicePort 22 127.0.0.1:22 ------------------ После этого в каталоге "/var/lib/tor" у вас появится новый каталог с тем произвольным именем onion.service Этот каталог будет содержать файлы hostname - ваше имя onion ПО: torsocks wget -q http://asdjasidhiashdojaspokppskjfpajppkspfvvbnbnahj13kldk.onion/file.html torsocks ssh asdjasidhiashdojaspokppskjfpajppkspfvvbnbnahj13kldk.onion |
Mesh-сети
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
Mesh-сеть - это распределенная одноранговая сеть, основанная на ячеистой топологии. Каждый узел в такой сети обладает такими же полномочиями, что и все остальные, груб говоря, все узлы в mesh-сети равны. Яркий представитель меш сети это Yggdrasil (есть альтернатива Riv-mesh работает по такому же принципу) Пиринговая - это название ячеистых одноранговых сетей, в которой каждый хост связывается с несколькими известными ему пирами, соседями по сети, через которые хост организует свой адаптивный роутинг в сеть. Yggdrasil - это пиринговая сеть, использующая IPv6-адресацию, весь трафик в которой шифруется несимметричным кодированием( с приватным и публичным ключами) Проект Yggdrasil использует адреса IPv6 из подсети 0200::/7, который никак не пересекается с IANA(2000::/3) Где брать: https://github.com/yggdrasil-network/yggdrasil-go Установка: Читать: https://yggdrasil-network.github.io/installation-linux-deb.html Ключи для деб: mkdir -p /usr/local/apt-keys gpg --fetch-keys https://neilalexander.s3.dualstack.eu-west-2.amazonaws.com/deb/key.txt gpg --export BC1BF63BD10B8F1A | sudo tee /usr/local/apt-keys/yggdrasil-keyring.gpg > /dev/null echo 'deb [signed-by=/usr/local/apt-keys/yggdrasil-keyring.gpg] http://neilalexander.s3.dualstack.eu-west-2.amazonaws.com/deb/ debian yggdrasil' | sudo tee /etc/apt/sources.list.d/yggdrasil.list Ставим: apt-get install dirmngr apt-get install yggdrasil systemctl enable yggdrasil systemctl start yggdrasil Конфиг: /etc/yggdrasil.conf Тут надо добавить в секцию "Peers {}" пиров с которыми будем взаимодействовать пиры смотри тут: https://github.com/yggdrasil-network/public-peers проверить этим: https://github.com/zhoreeq/peer_checker.py или вот апдейтер: https://github.com/ygguser/peers_updater cargo build --release cd traget/release ./peers_updater -p > peers.txt После перезапустить: systemctl restart yggdrasil Проверка какие пиры использует хост: yggdrasilctl getPeers Показать адрес и другую информацию: yggdrasilctl getSelf Обнаружена пиров: MPL(Multicast Peer Discovery) - технология автоматического обнаружения пиров порт UDP-9001 Для работы в локальной сети нужен настроенный IPv6 Для yggdrasil могут использоваться адреса вида 02XX и 03XX Трюк: Если в сети уже установлен клиент Yggdrasil можно сделать маршрутизацию через него для других пк в сети У вас на ПК с клиентом должен быть включен "IPv6 forward" Узнаем IPv6 на клиенте ygg например так: yggdrasilctl getSelf Например на адресс там: 31d:4cbf:9eaf:2399::/64 На устройстве где у нас Yddgrasil добавим адрес на интерфейс который смотрит в лакалку так: ip a a 31d:4cbf:9eaf:2399::1/64 dev eth1 включим ip forwarding: systemctl -w net.ipv6.conf.all.all.forwarding=1 На ПК который мы хотим научить ходить в ygg: ip a a 31d:4cbf:9eaf:2399::5/64 dev eth1 ip -6 route add 0200::/7 via 31d:4cbf:9eaf:2399::1 ping ygg.linux-ru.lib |
l2p
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
l2p - представляет собой изолированную автономную сеть Даркнет. https://github.com/PurpleI2P/i2pd apt isntall libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libssk-dev Сборка: cd /opt git clone https://github.com/PurpleI2P/i2pd.git cd i3pf/build cmake -DCMAKE_BUILD_TYPE=release make -j2 make install Для того что бы все заработало, нужно из каталога сборки скопировать конфиги в домашний каталог пользователя cp -R contrib/certificates $HOME/.i2pd cp contrib/i2pd.conf $HOME/.i2pd/ cp contrib/tunnels.conf $HOME/.i2pd/ теперь можно запустить i2pd проверяем доступность локальной веб консоли telnet 127.0.0.1 7070 Локальный хеша адресов можно обнаружить в каталоге "$HOME/.i2pd/addressbook" Для рганизации тунеля нужно будет настроить файл из .i2pd/tennels.conf |