Курсовая работа: Реализация сети в операционной системе Linux
sys_socket
tcp_close net/ipv4/tcp.c
устанавливает флаг FYN
tpc_connect net/ipv4/tpc_output.c
сохдает пакеты для соединения с установленным размером окна
и соответствующими битами, ложит пакет в очередь и выpывает
tcp_transmit_skb чтоб послать пакет
tcp_transmit_skb -заполняет заголовок пакета и передает его
на уроветь IP
tcp_v4_connect()
вызывает ip_route_connect
создает соединительный пакет и вызывает tcp_connect
udp_close
udp_connect
Обмен данными
Эта часть описывает процесс обмена данными между различными уровнями ядра и сети Когда приложение отправляет данные то оно пишет в сокет тот в своб очередь определяет свой тип и вызывает соответствующую функцию,та передает данные протоколу транспортного уровня(tcp,udp) функции етого уровня создают структуру sk_buff,копируют в неё данные заполняют заголовок своего уровня,считают контрольную сумму и шлют на уровень IP.Там дописывается заголовок ip,checksum,возможно пакет фраг менторуется и шлётся на xmit очередь сетевого девайса ,тот посылает пакет в сеть.
dev_queue_xmit() - net/core/dev.c
spin_lock_bh() -блокируем девайс
если у него есть очередь
calls enqueue() добавляем пакет
calls qdis() пробуждаем девайс
else calls dev->hard_start_xmit()
calls spin_unlock_bh() освобождаем девайс
DEVICE->hard_start_xmit() - зависит от девайса, drivers/net/DEVICE.c
в общем проверяет открыто ли устройство
посылает заголовок
говорит системной шине послать пакет
обновляет статус
inet_sendmsg() - net/ipv4/af_inet.c
int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
/*биндим сокет. */
if (sk->num==0 && inet_autobind(sk) != 0)
return -EAGAIN;
вызываем функцию протокола чтоб послать данные
return sk->prot->sendmsg(sk, msg, size);
}
ip_build_xmit - net/ipv4/ip_output.c (604)
calls sock_alloc_send_skb() выделяем память
=заголовочек=
if(!sk->protinfo.af_inet.hdrincl) {
iph->version=4;
iph->ihl=5;
iph->tos=sk->protinfo.af_inet.tos;
iph->tot_len = htons(length);
iph->frag_off = df;
iph->ttl=sk->protinfo.af_inet.mc_ttl;
ip_select_ident(iph, &rt->u.dst, sk);
if (rt->rt_type != RTN_MULTICAST)
iph->ttl=sk->protinfo.af_inet.ttl;
iph->protocol=sk->protocol;
iph->saddr=rt->rt_src;
iph->daddr=rt->rt_dst;
iph->check=0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
}
calls getfrag() копируем данные у юзера
returns rt->u.dst.output() [= dev_queue_xmit()]
ip_queue_xmit() - net/ipv4/ip_output.c (234)
cмотри маршрут
достраиваем ip заголовок
фрагментирум если надо
adds IP checksum
calls skb->dst->output() [= dev_queue_xmit()]
qdisc_restart() - net/sched/sch_generic.c (50)
вырываем пакет из очереди
calls dev->hard_start_xmit()
обновляем статистику
if если ошибка опять стввим пакет в очередь
sock_sendmsg() - net/socket.c (325)
проверяем права и всё такое
calls scm_sendmsg() [socket control message]
шлёмс данные
calls sock->ops[inet]->sendmsg() and destroys scm
>>> sock_write() - net/socket.c (399)
calls socki_lookup() accоциируем сокет с inode
заполняем заголовок сообщения
returns sock_sendmsg()
tcp_sendmsg() - net/ipv4/tcp.c (755)
ждемс соединения
skb = tcp_alloc_pskb память
calls csum_and_copy_from_user() делаем checksum & копируем
calls tcp_send_skb()
tcp_send_skb() - net/ipv4/tcp_output.c (160)
это главная routine посылки буфера
мы ставим буфер в очередь и решаем оставить его там или послать
calls __skb_queue_tail() добавляем в очередь
calls tcp_transmit_skb() если может
tcp_transmit_skb() - net/ipv4/tcp_output.c (77)
строим заголовок tcp и чексумму
calls tcp_build_and_update_options()
проверяем ACKs,SYN
calls tp->af_specific[ip]->queue_xmit()
udp_getfrag() - net/ipv4/udp.c
копируем из адресного пространства пользователя и добавляем checksum
udp_sendmsg() - net/ipv4/udp.c
проверяем флаги и тд
заполняем заголовок
проверяем мультикаст
заполняем маршутную информацию
calls ip_build_xmit()
обновляем статистику udp
returns err
Получение данных
Получение данных начинается с прерывания от сетевой карты. Драйвер девайса выделяет память и пересылает данные в то пространство. Потом передает пакет в связующий уровень который вызывает bottom-halv,которое обрабатывает событие вне прерывания пересылая данные на уровень выше -ip.Тот проверяет ошибки фрагменты, маршрутизирует пакет или отсылает на уровень выше(tcp || udp) Этот уровень снова проверяет ошибки определяет сокет которому предназначен пакет и ложит его в очередь сокета. Тот в свою очередь будит пользовательский процесс и копирует данные в его буфер.
Чтение из сокета(1)
Пытаемся что-то прочитать(и засыпаем)
Заполняем заголовок сообщения указателем на буфер(сокет)
проверяем простые ошибки
передаем сообщение inet сокету
Получение пакета
Пробуждение устройства(прерывание)
проверка девайса
Получение заголовка
выделение памяти
ложим пакет в то место судя по всему используя DMA
ставим пакет в очередь
выставляем флаг запуска bottom-halv
BottomHalv
Запуск сетевого ботом-халва
Пересылка пакетов из девайса чтоб не было прерываний
пересылка пакетов на уровень ip
очистка очереди отсылки
возврат
Уровень IP
Проверка ошибок
Дефрагментация если необходимо
Определение маршрута(форвардить или нет)
Отсылка пакета по назначению(TCP||UDP||forwarding)
Получение пакета в UDP
Проверка ошибок
проверка сокета назначения
пересылка пакета в очередь сокета
пробуждения ждущего процесса
Получение TCP
Проверка флагов и ошибок а также не был ли получен пакет ранее
Определение сокета
пересылка пакета в очередь сокета
пробуждения ждущего процесса
Чтение из сокета(2)
Пробуждение процесса
Вызов соответствуюшей функции доставки(udp ||tcp) в буфер пользователя
Возврат
IP forwarding
Рассмотрим подробнее процесс форвардинга пакетов
Сначала идет проверка TTL и уменьшение его на 1 Проверка пакета на наличие действительного маршрута если такого нет то отсылается соответствующее icmp сообщение копирование пакета в новый буфер и освобождение старого Установка нужных ip опций фрагменторование если необходимо отправка пакета на нужный девайс
DEVICE_rx() девайсно зависимая функция,
пример drivers/net/de600.c
здесь я попытаюсь перевести замечательные комментарии автора
Linux driver for the D-Link DE-600 Ethernet pocket adapter.
*
* Portions (C) Copyright 1993, 1994 by Bjorn Ekwall
* The Author may be reached as bj0rn@blox.se
/*
* Если у нас хороший пакет то забираем его из адаптера
*/
static void
de600_rx_intr(struct net_device *dev)
{
struct sk_buff *skb;
unsigned long flags;
int i;
int read_from;
int size;
register unsigned char *buffer;
save_flags(flags);
cli();
/* Определяем размер пакета */
size = de600_read_byte(RX_LEN, dev); /* нижния байт */
size += (de600_read_byte(RX_LEN, dev) << 8); /* верхний байт */
size -= 4; /* Ignore trailing 4 CRC-bytes */
/* Сообщаем адаптеру куда ложить следующий пакет и получаем */
read_from = rx_page_adr();
next_rx_page();
de600_put_command(RX_ENABLE);
restore_flags(flags);
if ((size < 32) || (size > 1535)) {
printk("%s: Bogus packet size %d.\n", dev->name, size);
if (size > 10000)
adapter_init(dev);
return;
}
skb = dev_alloc_skb(size+2);
if (skb == NULL) {
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
dev->name, size);
return;
}
/* Иначе*/
skb->dev = dev;
skb_reserve(skb,2); /* Align */
/* 'skb->data' указывет на начало буфера данных. */
buffer = skb_put(skb,size);
/* копируем пакет в буфер */
de600_setup_address(read_from, RW_ADDR);
for (i = size; i > 0; --i, ++buffer)
*buffer = de600_read_byte(READ_DATA, dev);
/* Определяем тип протокола
skb->protocol=eth_type_trans(skb,dev);
/*Передаем на верхний уровень см net/core/dev.c
netif_rx(skb);
/* обновляем статистику */
dev->last_rx = jiffies;
((struct net_device_stats *)(dev->priv))->rx_packets++; /* количество получений */
((struct net_device_stats *)(dev->priv))->rx_bytes += size; /* количество полученных байт */
/*
* Если случится что-то плохое во время доставки, netif_rx()
* сделало a mark_bh(INET_BH) для нас и будет работать
* когда мы войдем в bottom-halv.
*/
}
ip_finish_output() net/ipv4/ip_output
определяет девайс для данного маршрута
вызывает функцию девайса[=dev_queue_xmit]
ip_forward -net/ipv4/ip_forward
в этом файле хорошие комментарии
проверяем роутер
если пакет никому не предназначен то дропаем
если плохой TTL аналогично
если неможет пакет отфорвардится то отправляем icmp пакет ICMP_DEST_UNREACH
если необходимо шлем пакет ICMP HOST REDIRECT
копируем и уничтожаем старый пакет
уменьшаем TTL
если необходимо устанавливаем нужные опции ip_forward_options в
ip_forward_finish
ip_rcv net/ipv4/ip_input.c главная функция получения ip пакета
проверяем ошибки
плохая длина
версия
чексумма
вызываем pskb_trim
вызываем ip_route_input
Процесс маршрутизации
Как уже говорилось есть тоюлица соседей, FIB,routing cache Таблица соседей содержит адреса(mac) компьютеров которые физически соединены с нами. Linux использует АRP для определения адресов ета таблица динамическая хотя администраторы могут задать статические записи. Стуктуры связанные с етой таблицей описаны в include/net/neighbour.h основные структуры. struct neigh_table -их целый связаный список struct neigh_parms -список содержит разнообразную статистику struct neighbour -hash таблица соседей ассоциированных с данной таблицей struct pneig_entry -hash всех девайсов
поля struct neighbour
struct net_device -девайс
hh_cache -указатель на аппаратный кэш
sk_buff_head arp_queuq -очередь arp пакетов
есть local -в ней находятся свои интерфейсы
и main в ней наверное всё остальное
Forwarding Information Database
struct fib_table в include/net/ip_fib.h
содержит указатели на различные функции
tb_stamp
tb_id -255 для local и 254 для main
td_data -hash fib таблица
struct fn_hash -net/ipv4/fib_hash.c
struct fn_zone *fn_zones[33] -указатели на зоны
struct fn_zone *fn_zone_list указатель на первую не пустую зону
struct fn_zone содержит информацию про зону и маршруты для неё
struct fib_node ** fz_hash -указывает на кэш записей этой зоны
int fz_nent количество записей
int fx_divisor числу бакетов для зоны (в основном 16 кроме зоны 0000
loopback девайса)
int fz_order индекс зоны в родительской fn_hash
struct fib_node -содержит информацию по девайсу в fib_info(include/net/ip_fib.h)
метрику ,протокол и т.д
Routing Cache
Это наиболее быстрый способ нахождения маршрута Когда ip нужен маршрут ,то он определяет ячейку в хэше,которая указывает на цепочку маршрутов и идёт по этой цепочке пока не найдет нужный маршруты имеют таймеры и частоту использования ,наиболее частые перемещаются в начало.
struct rtable -звено в цепочке
содержит адреса отправителя и получателя
входящий интерфейс
адрес соседа или шлюза
struct dst_entry
содержит спецефические для данного маршрута данные и функции
struct dev -понятно
pmtu максимальная длина пакета для данного маршрута
int (*input)(struct sk_buff) -указатель на функцию приема для данного маршрута
часто ето tcp_rcv
int (*output)(struct sk_buff) указатель на функцию отсылки (dev_queue_xmit)
также разнообразные статистические данные и опции
Таким образом нами было проведено исследование сетевой архитектуры операционной системы Линух на примере реализации стека протоколов tcp-ip версии 4 в ядре 2.4.7
Приложение
После длительных теоретических изысканий применим их на практике
Нашей целью будет создание удобного пользовательского интерфейса для указания в пакете подставного ip адреса(адреса которого нет у никакого нашего интерфейса) Я не буду показывать ,то как адреса выставляются в ядре. Замечу только то что, из сокета семейства AF_INET и типа SOCK_RAW пакет с не своим адресом отправить вроде бы можно (в ядре 2.2 ,насчет 2.4 неуверен -может там есть какие-то проверки). страницы мана говорят про опцию IP_HDRINCL .Их можно отправлять также через тип SOCK_PACKET. Но для всего этого знать код ядра не очень необходимо. Поэтому мы пойдём други путём.
Наиболее легкий путь(?) сделать это через интерфейс setsockopt. После внимательного изучения кода функции sys_setsockopt -net/socket.c находим строки if ((sock = sockfd_lookup(fd, &err))!=NULL)
{
if (level == SOL_SOCKET)
err=sock_setsockopt(sock,level,optname,optval,optlen);
else
err=sock->ops->setsockopt(sock, level, optname, optval,optlen);
sockfd_put(sock);
}
return err;
}
значит нам надо искать функцию setsockopt в коде для реализации для типа sock_raw это файл net/ipv4/raw.c смотрим static int raw_setsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen)
{
if (level != SOL_RAW)
return ip_setsockopt(sk, level, optname, optval, optlen);
...................................
}
функция ip_setsockopt лежит в net/ipv4/ip_sockglue.c в ней идет длинный перебор опций мы остановим свой выбор на уровне SOL_IP и добавим в перебор свои строки /*HACK:>>>>>>>>>>>>>>>*/
#ifdef CONFIG_HACKIP
case IP_HACKIP:
printk("HACKIP:setsockopt flag %d\n",sk->hackflag);
sk->hackflag=1;
get_user(val,(int *) optval);
printk("HACKIP:setsockopt val %d\n",val);
sk->hackf.src_addr=val;
break;
#endif
case IP_HDRINCL:
подробнее опишем происходящие действия
printk -выводим отлабочные сообщения
Я не уверен ,но судя по всему при создании сокета вся структура обнуляется поэтому мы можем не смотреть флаг .Я добавил эту строку ,чтоб посмотреть всегда ли он равен 0 при не установленной опции а после установки при повторе он равен 1. get_user забираем значение ,подробности include/asm/uaccess.h но для всего этого нам надо добавить соответствующие поля в struct sock =======sock.h=============
.........................
#ifdef CONFIG_HACKIP
/*HACK:>>>>>>>>>>>>>>>>>>*/
struct ip_hack {
__u32 src_addr;
};
#endif
struct sock {
/* Socket demultiplex comparisons on incoming packets. */
.................................
#ifdef CONFIG_HACKIP
/*HACK:>>>>>>>>>>>>>>>>>*/
struct ip_hack hackf;
int hackflag;
#endif
........................................
===========end======================
теперь нам надо перехватить отправку пакета
идем в файл net/ipv4/ip_output.c и после всех строк где есть 'iph->saddr=' вставляем наш код #ifdef CONFIG_HACKIP
if((sk->hackf.src_addr!=0)&&(sk->hackflag==1))
{
iph->saddr=sk->hackf.src_addr;
printk("HACKIP:ip_build_and_send.. %d\n",iph->saddr);
}
#endif
Осталось малое: в файл include/linux/in.h добавляем строку #define IP_HACKIP 16
в файл net/Config.in
bool 'HACKIP facilities' CONFIG_HACKIP делаем
cd /usr/src/linux
make menuconfig
make dep
make bzImage
cp arh/i386/boot/bzImage /boot/kursach
правим lilo.conf или /boot/grub/menu.lst
соответствуюшая команда
reboot....
теперь протестируем нашу программу извиняюсь за возможное наличие лишних include просто я переделал файл из друго-го проекта
============rel.c========================
/* Written by Gleb Paharenko <gleb@ptf.kiev.ua> 2003 */
/*Посвящяется Кевину Митнику */
/*и прекрасной весне в мае 2003-го*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<resolv.h>
#include<arpa/inet.h>
#include<errno.h>
#include<string.h>
#include<linux/ip.h>
#define IP_HACKIP 16
int main()
{
int sd,res;
int value=1;
int sval=0;
int oval=1;
char buffer[100];
struct sockaddr_in addr,raddr;
bzero(buffer,sizeof(buffer));
if((sd=socket(PF_INET,SOCK_RAW,6))<0)
{
perror("Socket");
exit(errno);
}
bzero(&addr,sizeof(addr));
addr.sin_family=AF_INET;
raddr.sin_family=AF_INET;
addr.sin_port=0;
raddr.sin_port=0;
inet_aton("212.168.1.11",(struct sockaddr *)&(addr.sin_addr));
inet_aton("192.168.1.1",(struct sockaddr *)&(raddr.sin_addr));
sval=addr.sin_addr.s_addr;
inet_aton("192.168.1.10",(struct sockaddr *)&(addr.sin_addr));
if(bind(sd,(struct sockaddr *)&addr,sizeof(addr))<0)
{
perror("bind");
exit(errno);
}
if(connect(sd,(struct sockaddr *)&raddr,sizeof(raddr))!=0){
perror("connect");exit(errno);}
/* Вот ОНО!*/
if(setsockopt(sd,SOL_IP,IP_HACKIP,&sval,4)!=0)
{ perror("setsockopt");
exit(errno);
}
send(sd,"Kursovaja",10,0);
}
делаем
# gcc rel.c
#./a.out
#tail /var/log/messages
..................
..................
May 20 00:53:49 kursach -- root[863]: ROOT LOGIN ON tty1
May 20 00:53:51 kursach kernel: HACKIP:setsockopt flag 0
May 20 00:53:51 kursach kernel: HACKIP:setsockopt val 184658132
May 20 00:53:51 kursach kernel: HACKIP:ip_build_and_send.. 184658132
Обьясняю
дома у меня стоит vmware :host-only networking
host machine Windows2000 Professional 192.168.1.1/24
virtual Linux Red-Hat 7.2 "Enigma" 192.168.1.10/24
на 2000 запущен SpyNet
Ловим пакет и...
Работает!!!!!
Список литературы
Для подготовки данной работы были использованы материалы с сайта http://www.winsov.ru