Docsity
Docsity

Подготовься к экзаменам
Подготовься к экзаменам

Учись благодаря многочисленным ресурсам, которые есть на Docsity


Получи баллы для скачивания
Получи баллы для скачивания

Заработай баллы, помогая другим студентам, или приобретай их по тарифом Премиум


Руководства и советы
Руководства и советы

Реализация сети в операционной системе Linux курсовая по информатике , Дипломная из Информатика

Реализация сети в операционной системе Linux курсовая по информатике

Вид: Дипломная

2016/2017

Загружен 11.04.2017

refbank10317
refbank10317 🇷🇺

5

(1)

11 документы

1 / 25

Toggle sidebar

Сопутствующие документы


Частичный предварительный просмотр текста

Скачай Реализация сети в операционной системе Linux курсовая по информатике и еще Дипломная в формате PDF Информатика только на Docsity! Реализация сети в операционной системе Linux Глеб Пахаренко Рассмотрим подробнее что происходит с пакетом при попадании в нашу машину. Сначала он обрабатывается драйвером аппаратуры(сетевой карты и т.д) если пакет предназначен нам то он посылается на выше лежащий уровень - сетевой там определяется для кого он предназначен: нам или кому-то другому, для этого просматривается кэш маршрутизации, если там нет маршрута то Forwarding Information Base (FIB), если пакет предназначен другому компьютеру то ядро шлёт его на соответствующее устройство (сетевую карту) ,если нам ,то через транспортный и вышележащие уровни приложению. Обмен данными между приложением и ядром осуществляется через абстракцию сокета. В Линухе используется BSD сокеты. Рассмотрим поподробнее структуру пакета Ключ к быстрому обмену данными в использовании структуры sk_buf и передачи на вышестоящие уровни только указателя на неё описание структуры лежит в linux/skbuff.h её поля struct sk_buff { /* These two members must be first. */ struct sk_buff * next; /* Next buffer in list */ struct sk_buff * prev; /* Previous buffer in list */ struct sk_buff_head * list; /* List we are on */ struct sock *sk; /* Socket we are owned by */ struct timeval stamp; /* Time we arrived */ struct net_device *dev; /* Device we arrived on/are leaving by */ /* Transport layer header */ union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; struct spxhdr *spxh; unsigned char *raw; } h; /* Network layer header */ union { struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; struct ipxhdr *ipxh; unsigned char *raw; } nh; /* Link layer header */ union { struct ethhdr *ethernet; unsigned char *raw; } mac; struct dst_entry *dst; /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ char cb[48]; unsigned int len; /* Length of actual data */ unsigned int data_len; unsigned int csum; /* Checksum */ unsigned char __unused, /* Dead field, may be reused */ cloned, /* head may be cloned (check refcnt to be sure). */ pkt_type, /* Packet class */ ip_summed; /* Driver fed us an IP checksum */ __u32 priority; /* Packet queueing priority */ atomic_t users; /* User count - see datagram.c,tcp.c */ unsigned short protocol; /* Packet protocol from driver. */ unsigned short security; /* Security level of packet */ unsigned int truesize; /* Buffer size */ unsigned char *head; /* Head of buffer */ unsigned char *data; /* Data head pointer */ unsigned char *tail; /* Tail pointer */ unsigned char *end; /* End pointer */ void (*destructor)(struct sk_buff *); /* Destruct function */ #ifdef CONFIG_NETFILTER /* Can be used for communication between hooks. */ unsigned long nfmark; struct inode *inode; struct fasync_struct *fasync_list; /* Asynchronous wake up list */ struct file *file; /* File back pointer for gc */ struct sock *sk; wait_queue_head_t wait; short type; unsigned char passcred; }; struct proto_ops { int family; int (*release) (struct socket *sock); int (*bind) (struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len); int (*connect) (struct socket *sock, struct sockaddr *uservaddr, int sockaddr_len, int flags); int (*socketpair) (struct socket *sock1, struct socket *sock2); int (*accept) (struct socket *sock, struct socket *newsock, int flags); int (*getname) (struct socket *sock, struct sockaddr *uaddr, int *usockaddr_len, int peer); unsigned int (*poll) (struct file *file, struct socket *sock, struct poll_table_struct *wait); int (*ioctl) (struct socket *sock, unsigned int cmd, unsigned long arg); int (*listen) (struct socket *sock, int len); int (*shutdown) (struct socket *sock, int flags); int (*setsockopt) (struct socket *sock, int level, int optname, char *optval, int optlen); int (*getsockopt) (struct socket *sock, int level, int optname, char *optval, int *optlen); int (*sendmsg) (struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm); int (*recvmsg) (struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm); int (*mmap) (struct file *file, struct socket *sock, struct vm_area_struct * vma); ssize_t (*sendpage) (struct socket *sock, struct page *page, int offset, size_t size, int flags); }; наиболее важные поля * struct proto_ops *ops указывает на протокольно зависимые функции struct inode на inode файла сокета struct sock* на инет сокет INET net/sock.h struct sock struct sock { /* Socket demultiplex comparisons on incoming packets. */ __u32 daddr; /* Foreign IPv4 addr */ __u32 rcv_saddr; /* Bound local IPv4 addr */ __u16 dport; /* Destination port */ unsigned short num; /* Local port */ int bound_dev_if; /* Bound device index if != 0 */ /* Main hash linkage for various protocol lookup tables. */ struct sock *next; struct sock **pprev; struct sock *bind_next; struct sock **bind_pprev; volatile unsigned char state, /* Connection state */ zapped; /* In ax25 & ipx means not linked */ __u16 sport; /* Source port */ unsigned short family; /* Address family */ unsigned char reuse; /* SO_REUSEADDR setting */ unsigned char shutdown; atomic_t refcnt; /* Reference count */ socket_lock_t lock; /* Synchronizer... */ int rcvbuf; /* Size of receive buffer in bytes */ wait_queue_head_t *sleep; /* Sock wait queue */ struct dst_entry *dst_cache; /* Destination cache */ rwlock_t dst_lock; atomic_t rmem_alloc; /* Receive queue bytes committed */ struct sk_buff_head receive_queue; /* Incoming packets */ atomic_t wmem_alloc; /* Transmit queue bytes committed */ struct sk_buff_head write_queue; /* Packet sending queue */ atomic_t omem_alloc; /* "o" is "option" or "other" */ int wmem_queued; /* Persistent queue size */ int forward_alloc; /* Space allocated forward. */ __u32 saddr; /* Sending source */ unsigned int allocation; /* Allocation mode */ int sndbuf; /* Size of send buffer in bytes */ struct sock *prev; /* Not all are volatile, but some are, so we might as well say they all are. * XXX Make this a flag word -DaveM */ volatile char dead, done, urginline, keepopen, linger, destroy, no_check, broadcast, bsdism; unsigned char debug; unsigned char rcvtstamp; unsigned char use_write_queue; unsigned char userlocks; /* Hole of 3 bytes. Try to pack. */ int route_caps; int proc; unsigned long lingertime; int hashent; struct sock *pair; /* The backlog queue is special, it is always used with * the per-socket spinlock held and requires low latency * access. Therefore we special case it's implementation. */ struct { struct sk_buff *head; struct sk_buff *tail; } backlog; rwlock_t callback_lock; /* Error queue, rarely used. */ struct sk_buff_head error_queue; struct proto *prot; #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) union { struct ipv6_pinfo af_inet6; } net_pinfo; #endif struct irda_sock *irda; #endif #if defined(CONFIG_WAN_ROUTER) || defined(CONFIG_WAN_ROUTER_MODULE) struct wanpipe_opt *af_wanpipe; #endif } protinfo; /* This part is used for the timeout functions. */ struct timer_list timer; /* This is the sock cleanup timer. */ struct timeval stamp; /* Identd and reporting IO signals */ struct socket *socket; /* RPC and TUX layer private data */ void *user_data; /* Callbacks */ void (*state_change)(struct sock *sk); void (*data_ready)(struct sock *sk,int bytes); void (*write_space)(struct sock *sk); void (*error_report)(struct sock *sk); int (*backlog_rcv) (struct sock *sk, struct sk_buff *skb); void (*destruct)(struct sock *sk); }; Эта структура очень широко используется и имеет много hacks зависящих от конфигурации как видим для каждого протокола здесь найдется местечко Сокеты проходят через процесс маршрутизации только один раз для каждого маршрута. Они содержат указатель на маршрут struct sock- >dst_cache* и вызывают ip_route_connect (net/route.h) для нахождения маршрута информация записывается в dst_cache и сокет дальше использует её не повторяя операции поиска маршрута пока не случится что-то необычное в этом и есть смысл connect Установление соединения Рассмотрим стандартный пример /* look up host */ server = gethostbyname(SERVER_NAME); /* get socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); /* set up address */ address.sin_family = AF_INET; address.sin_port = htons(PORT_NUM); memcpy(&address.sin_addr,server->h_addr,server->h_length); /* connect to server */ connect(sockfd, &address, sizeof(address)); socket создаёт обект сокета определенного типа и инициализирует его также делает дефолтовские очереди (incoming,outgoing,error,backlog) и заголовок TCP connect определяет маршруты вызывая протокольно зависимые функции (tcp_v4_connect(),udp_connect()) net/socket.c asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, int addrlen) { ................................ err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen, sock->file->f_flags); .......................... } int sock_create(int family, int type, int protocol, struct socket **res) { ..................................... //cоздаем протокольно зависимый сокет! //-------------------------------------- if ((i = net_families[family]->create(sock, protocol)) < 0) { sock_release(sock); goto out; } ................. } Функции Socket Проверяем ошибки Выделяем память Ложим сокет в список inode Устанавливаем указатели на протокольно зависимые части Сохраняем данные про тип и параметры сокета Устанавливаем сокет в положение закрыт Инициализируем очереди пакетов Connect Проверяем ошибки Определяем Маршрут Проверяем кэш Смотрим в FIB Создаем новую запись в таблице маршрутизации Заполняем её и возвращаем Сохраняем указатель на запись маршрутизации в сокете Вызываем протокольно зависимую функцию connect Устанавливаем сокет в соединенный Также надо не забыть закрыть сокет Close вызывает sock_close in socket.c void sock_release(struct socket *sock) { if (sock->ops) sock->ops->release(sock); ........................... } а та через цепочку вызовов протокольнозависимую функцию Дополнительные функции void inet_sock_release(struct sock *sk) -net/ipv4/af_inet.c назвние говорит за себя + хороший комментарий Алана Коха fib_lookup() - include/net/ip_fib.h возвращает маршрут .Написана русским -Кузнецов! fn_hach_lookup net/fib_hash.c возвращает маршрут по адресу inet_create net/ipv4/af_inet.c создает сокет inet_release <||> ip_route_connect вызывает ip_route_output для определении адреса назначения ip_route_output ip_route_output_slow rt_intern_hash полезные для маршрутизации функции sock_close() sock_create() sock_init_data net/core/sock.c инициализирует основные поля сокета sock_release net/socket.c sys_socket tcp_close net/ipv4/tcp.c 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 */ 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
Docsity logo