Реализация сети в операционной системе Linux
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 [email protected]
/*
* Если у нас хороший пакет то забираем его из адаптера
*/
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) 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 %dn",sk->hackflag);
sk->hackflag=1;
get_user(val,(int *) optval);
printk("HACKIP:setsockopt val %dn",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.. %dn",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 2003 */
/*Посвящяется Кевину Митнику */
/*и прекрасной весне в мае 2003-го*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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))