Реалізація мережі в операційній системі Linux h2>
Гліб Пахаренко p>
Розглянемо
докладніше що відбувається з пакетом при попаданні в нашу машину. Спочатку він
обробляється драйвером апаратури (мережевої карти і т.д) якщо пакет
призначений нам то він посилається на вище лежить рівень - мережевий там
визначається для кого він призначений: нам або комусь іншому, для цього
проглядається кеш маршрутизації, якщо там немає маршруту то Forwarding Information
Base (FIB), якщо пакет призначений іншого комп'ютера те ядро шле його на
відповідний пристрій (мережеву карту), якщо нам, то через транспортний і
вищерозміщені рівні з додатком. Обмін даними між додатком і ядром
здійснюється через абстракцію сокета. У Линух використовується BSD сокет. P>
Розглянемо
детальніше структуру пакета p>
Ключ
до швидкого обміну даними у використанні структури sk_buf і передачі на
вищі рівні тільки вказівника на неї p>
опис
структури лежить в linux/skbuff.h p>
її поля p>
struct sk_buff ( p>
/* These two members must be first.
*/ p>
struct sk_buff * next;/* Next
buffer in list */ p>
struct sk_buff * prev;/* Previous
buffer in list */ p>
struct sk_buff_head * list;/* List
we are on */ p>
struct sock * sk;/* Socket we are
owned by */ p>
struct timeval stamp;/* Time we
arrived */ p>
struct net_device * dev;/* Device we
arrived on/are leaving by */ p>
/* Transport layer header */ p>
union p>
( p>
struct tcphdr * th; p>
struct udphdr * uh; p>
struct icmphdr * icmph; p>
struct igmphdr * igmph; p>
struct iphdr * ipiph; p>
struct spxhdr * spxh; p>
unsigned char * raw; p>
) h; p>
/* Network layer header */ p>
union p>
( p>
struct iphdr * iph; p>
struct ipv6hdr * ipv6h; p>
struct arphdr * arph; p>
struct ipxhdr * ipxh; p>
unsigned char * raw; p>
) nh; p>
/* Link layer header */ p>
union p>
( p>
struct ethhdr * ethernet; p>
unsigned char * raw; p>
) mac; p>
struct dst_entry * dst; p>
/* p>
* This is the control buffer. It is
free to use for every p>
* layer. Please put your private
variables there. If you p>
* want to keep them across layers
you have to do a skb_clone () p>
* first. This is owned by whoever
has the skb queued ATM. p>
*/ p>
char cb [48]; p>
unsigned int len;/* Length of
actual data */ p>
unsigned int data_len; p>
unsigned int csum;/* Checksum */ p>
unsigned char __unused,/* Dead
field, may be reused */ p>
cloned,/* head may be cloned (check
refcnt to be sure). */ p>
pkt_type,/* Packet class */ p>
ip_summed;/* Driver fed us an IP
checksum */ p>
__u32 priority;/* Packet queueing
priority */ p>
atomic_t users;/* User count - see
datagram.c, tcp.c */ p>
unsigned short protocol;/* Packet
protocol from driver. */ p>
unsigned short security;/* Security
level of packet */ p>
unsigned int truesize;/* Buffer
size */ p>
unsigned char * head;/* Head of
buffer */ p>
unsigned char * data;/* Data head
pointer */ p>
unsigned char * tail;/* Tail pointer
*/ p>
unsigned char * end;/* End pointer
*/ p>
void (* destructor) (struct sk_buff
*);/* Destruct function */ p>
# ifdef CONFIG_NETFILTER p>
/* Can be used for communication
between hooks. */ p>
unsigned long nfmark; p>
/* Cache info */ p>
__u32 nfcache; p>
/* Associated connection, if any */ p>
struct nf_ct_info * nfct; p>
# ifdef CONFIG_NETFILTER_DEBUG p>
unsigned int nf_debug; p>
# endif p>
# endif/* CONFIG_NETFILTER */ p>
# if defined (CONFIG_HIPPI) p>
union ( p>
__u32 ifield; p>
) private; p>
# endif p>
# ifdef CONFIG_NET_SCHED p>
__u32 tc_index;/* traffic control
index */ p>
# endif p>
); p>
там
ж міститься маса корисних функцій для роботи з sk_buff. всі протоколи використовують ету структуру додаючи заголовки свого
рівня p>
Маршрутизація
p>
Рівень
IP використовує
3 структури для маршрутизації FIB де зберігаються всі маршрути routing cache де знаходяться
найбільш часто використовувані neibour table список комп'ютерів фізично з'єднаних з даними p>
FIB містить
32 зони по одній на кожен біт ip адреси кожна зона містить точки входу для хостів та мереж які
задайтся даної маскою підмережі 255.0.0.0 має 8 значущих біт і тому у восьмий
зоні 255.255.255.0 в 24 зоні p>
файл/proc/net/route містить FIB p>
routing cache хеш-таблиця
то він додається туди з FIB застарілі записи після закінчення деякого часу видаляються вміст
кеша можна побачити в/proc/net/rt_cache p>
Ініціалізація
мережі p>
головні
налаштування мережі в дистрибутиві RedHat
(Mandrake) лежать в/etc/sysconfig/network,/etc/sysconfig/network-scripts/ifcfg-eth0 і тд ... p>
вміст
моїх файлів (не в virtual mashine редхате а на нормальній машині Mandrake-8.2 де відповідно немає ніяких
мережевих карт) p>
/etc/sysconfig/network p>
NETWORKING = yes p>
FORWARD_IPV4 = false p>
HOSTNAME = freeland.linux p>
DOMAINNAME = linux p>
/etc/sysconfig/network-scripts/ifcfg-lo
p>
DEVICE = lo p>
IPADDR = 127.0.0.1 p>
NETMASK = 255.0.0.0 p>
NETWORK = 127.0.0.0 p>
# If you're having problems with
gated making 127.0.0.0/8 a martian, p>
# you can change this to something
else (255.255.255.255, for example) p>
BROADCAST = 127.255.255.255 p>
ONBOOT = yes p>
NAME = loopback p>
Дуже
корисної програмою є ifconfig синтаксис якій детально розглянуто в мануалі p>
[20:16] [pts1]/etc/sysconfig/network-scripts
[root] p>
# ifconfig p>
lo Link encap: Local Loopback p>
inet addr: 127.0.0.1 Mask: 255.0.0.0 p>
UP LOOPBACK RUNNING MTU: 16436
Metric: 1 p>
RX packets: 3242 errors: 0 dropped: 0
overruns: 0 frame: 0 p>
TX packets: 3242 errors: 0 dropped: 0
overruns: 0 carrier: 0 p>
collisions: 0 txqueuelen: 0 p>
RX bytes: 227644 (222.3 Kb) TX
bytes: 227644 (222.3 Kb) p>
НЕ
менш корисна команда route p>
# route p>
Kernel IP routing table p>
Destination Gateway Genmask Flags
Metric Ref Use Iface p>
127.0.0.0
* 255.0.0.0 U 0 0 0 lo p>
її
призначення, а також багатьох інших описано в Linux Network Administrator Guide p>
З'єднання
p>
В
цій частині ми детально розглянемо сокета і все що з ними пов'язано p>
Коли
процес створює сокет то він порожній потім система визначає маршрут до віддаленого
хосту і вносить цю інформацію в сокет. Після цього пакети направляються на потрібне
пристрій p>
Є
два типи сокетов BSD сокети які включають як член INET cокети BSD сокети описуються
структурою struct socket в linux/net.h p>
struct socket p>
( p>
socket_state state; p>
unsigned long flags; p>
struct proto_ops * ops; p>
struct inode * inode; p>
struct fasync_struct * fasync_list;
/ * Asynchronous wake up list */ p>
struct file * file;/* File back
pointer for gc */ p>
struct sock * sk; p>
wait_queue_head_t wait; p>
short type; p>
unsigned char passcred; p>
); p>
struct proto_ops ( p>
int family; p>
int (* release) (struct socket
* sock); p>
int (* bind) (struct socket * sock,
struct sockaddr * umyaddr, p>
int sockaddr_len); p>
int (* connect) (struct socket * sock,
struct sockaddr * uservaddr, p>
int sockaddr_len, int flags); p>
int (* socketpair) (struct socket
* sock1, struct socket * sock2); p>
int (* accept) (struct socket * sock,
struct socket * newsock, p>
int flags); p>
int (* getname) (struct socket * sock,
struct sockaddr * uaddr, p>
int * usockaddr_len, int peer); p>
unsigned int (* poll) (struct file
* file, struct socket * sock, struct poll_table_struct * wait); p>
int (* ioctl) (struct socket * sock,
unsigned int cmd, p>
unsigned long arg); p>
int (* listen) (struct socket * sock,
int len); p>
int (* shutdown) (struct socket
* sock, int flags); p>
int (* setsockopt) (struct socket
* sock, int level, int optname, p>
char * optval, int optlen); p>
int (* getsockopt) (struct socket
* sock, int level, int optname, p>
char * optval, int * optlen); p>
int (* sendmsg) (struct socket * sock,
struct msghdr * m, int total_len, struct scm_cookie * scm); p>
int (* recvmsg) (struct socket * sock,
struct msghdr * m, int total_len, int flags, struct scm_cookie * scm); p>
int (* mmap) (struct file * file,
struct socket * sock, struct vm_area_struct * vma); p>
ssize_t (* sendpage) (struct socket
* sock, struct page * page, int offset, size_t size, int flags); p>
); p>
найбільш
важливі поля p>
*
struct
proto_ops * ops вказує на
протокольно залежні функції p>
struct inode на inode файлу сокета p>
struct sock * на инет сокет p>
INET net/sock.h struct sock p>
struct sock ( p>
/* Socket demultiplex comparisons on
incoming packets. */ p>
__u32 daddr;/* Foreign IPv4 addr */ p>
__u32 rcv_saddr;/* Bound local IPv4
addr */ p>
__u16 dport;/* Destination port */ p>
unsigned short num;/* Local port */ p>
int bound_dev_if;/* Bound device
index if! = 0 */ p>
/* Main hash linkage for various
protocol lookup tables. */ p>
struct sock * next; p>
struct sock ** pprev; p>
struct sock * bind_next; p>
struct sock ** bind_pprev; p>
volatile unsigned char state,/*
Connection state */ p>
zapped;/* In ax25 & ipx means
not linked */ p>
__u16 sport;/* Source port */ p>
unsigned short family;/* Address
family */ p>
unsigned char reuse;/* SO_REUSEADDR
setting */ p>
unsigned char shutdown; p>
atomic_t refcnt;/* Reference count
*/ p>
socket_lock_t lock;/*
Synchronizer ... */ p>
int rcvbuf;/* Size of receive
buffer in bytes */ p>
wait_queue_head_t * sleep;/* Sock
wait queue */ p>
struct dst_entry * dst_cache;/*
Destination cache */ p>
rwlock_t dst_lock; p>
atomic_t rmem_alloc;/* Receive
queue bytes committed */ p>
struct sk_buff_head receive_queue;
/ * Incoming packets */ p>
atomic_t wmem_alloc;/* Transmit
queue bytes committed */ p>
struct sk_buff_head write_queue;/*
Packet sending queue */ p>
atomic_t omem_alloc;/*
"o" is "option" or "other" */ p>
int wmem_queued;/* Persistent queue
size */ p>
int forward_alloc;/* Space
allocated forward. */ p>
__u32 saddr;/* Sending source */ p>
unsigned int allocation;/*
Allocation mode */ p>
int sndbuf;/* Size of send buffer
in bytes */ p>
struct sock * prev; p>
/* Not all are volatile, but some
are, so we might as well say they all are. p>
* XXX Make this a flag word-DaveM p>
*/ p>
volatile char dead, p>
done, p>
urginline, p>
keepopen, p>
linger, p>
destroy, p>
no_check, p>
broadcast, p>
bsdism; p>
unsigned char debug; p>
unsigned char rcvtstamp; p>
unsigned char use_write_queue; p>
unsigned char userlocks; p>
/* Hole of 3 bytes. Try to pack. */ p>
int route_caps; p>
int proc; p>
unsigned long lingertime; p>
int hashent; p>
struct sock * pair; p>
/* The backlog queue is special, it
is always used with p>
* the per-socket spinlock held and
requires low latency p>
* access. Therefore we special case
it's implementation. p>
*/ p>
struct ( p>
struct sk_buff * head; p>
struct sk_buff * tail; p>
) backlog; p>
rwlock_t callback_lock; p>
/* Error queue, rarely used. */ p>
struct sk_buff_head error_queue; p>
struct proto * prot; p>
# if defined (CONFIG_IPV6) | | defined
(CONFIG_IPV6_MODULE) p>
union ( p>
struct ipv6_pinfo af_inet6; p>
) net_pinfo; p>
# endif p>
union ( p>
struct tcp_opt af_tcp; p>
# if defined (CONFIG_INET) | | defined
(CONFIG_INET_MODULE) p>
struct raw_opt tp_raw4; p>
# endif p>
# if defined (CONFIG_IPV6) | | defined
(CONFIG_IPV6_MODULE) p>
struct raw6_opt tp_raw; p>
# endif/* CONFIG_IPV6 */ p>
# if defined (CONFIG_SPX) | | defined
(CONFIG_SPX_MODULE) p>
struct spx_opt af_spx; p>
# endif/* CONFIG_SPX */ p>
) tp_pinfo; p>
int err, err_soft;/* Soft holds
errors that don't p>
cause failure but are the cause p>
of a persistent failure not just p>
'timed out' */ p>
unsigned short ack_backlog; p>
unsigned short max_ack_backlog; p>
__u32 priority; p>
unsigned short type; p>
unsigned char localroute;/* Route
locally only */ p>
unsigned char protocol; p>
struct ucred peercred; p>
int rcvlowat; p>
long rcvtimeo; p>
long sndtimeo; p>
# ifdef CONFIG_FILTER p>
/* Socket Filtering Instructions */ p>
struct sk_filter * filter; p>
# endif/* CONFIG_FILTER */ p>
/* This is where all the private
(optional) areas that don't p>
* overlap will eventually live. p>
*/ p>
union ( p>
void * destruct_hook; p>
struct unix_opt af_unix; p>
# if defined (CONFIG_INET) | | defined
(CONFIG_INET_MODULE) p>
struct inet_opt af_inet; p>
# endif p>
# if defined (CONFIG_ATALK) | |
defined (CONFIG_ATALK_MODULE) p>
struct atalk_sock af_at; p>
# endif p>
# if defined (CONFIG_IPX) | |
defined (CONFIG_IPX_MODULE) p>
struct ipx_opt af_ipx; p>
# endif p>
# if defined (CONFIG_DECNET) | |
defined (CONFIG_DECNET_MODULE) p>
struct dn_scp dn; p>
# endif p>
# if defined (CONFIG_PACKET) | |
defined (CONFIG_PACKET_MODULE) p>
struct packet_opt * af_packet; p>
# endif p>
# if defined (CONFIG_X25) | |
defined (CONFIG_X25_MODULE) p>
x25_cb * x25; p>
# endif p>
# if defined (CONFIG_AX25) | |
defined (CONFIG_AX25_MODULE) p>
ax25_cb * ax25; p>
# endif p>
# if defined (CONFIG_NETROM) | |
defined (CONFIG_NETROM_MODULE) p>
nr_cb * nr; p>
# endif p>
# if defined (CONFIG_ROSE) | |
defined (CONFIG_ROSE_MODULE) p>
rose_cb * rose; p>
# endif p>
# if defined (CONFIG_PPPOE) | |
defined (CONFIG_PPPOE_MODULE) p>
struct pppox_opt * pppox; p>
# endif p>
# ifdef CONFIG_NETLINK p>
struct netlink_opt * af_netlink; p>
# endif p>
# if defined (CONFIG_ECONET) | |
defined (CONFIG_ECONET_MODULE) p>
struct econet_opt * af_econet; p>
# endif p>
# if defined (CONFIG_ATM) | |
defined (CONFIG_ATM_MODULE) p>
struct atm_vcc * af_atm; p>
# endif p>
# if defined (CONFIG_IRDA) | |
defined (CONFIG_IRDA_MODULE) p>
struct irda_sock * irda; p>
# endif p>
# if defined (CONFIG_WAN_ROUTER) | |
defined (CONFIG_WAN_ROUTER_MODULE) p>
struct wanpipe_opt * af_wanpipe; p>
# endif p>
) protinfo; p>
/* This part is used for the timeout
functions. */ p>
struct timer_list timer;/* This is
the sock cleanup timer. */ p>
struct timeval stamp; p>
/* Identd and reporting IO signals
*/ p>
struct socket * socket; p>
/* RPC and TUX layer private data */ p>
void * user_data; p>
/* Callbacks */ p>
void (* state_change) (struct sock
* sk); p>
void (* data_ready) (struct sock
* sk, int bytes); p>
void (* write_space) (struct sock
* sk); p>
void (* error_report) (struct sock
* sk); p>
int (* backlog_rcv) (struct sock * sk, p>
struct sk_buff * skb); p>
void (* destruct) (struct sock * sk); p>
); p>
Ця
структура дуже широко використовується і має багато hacks залежних від
конфігурації як бачимо для кожного протоколу тут знайдеться містечко p>
Сокети
проходять через процес маршрутизації тільки один раз для кожного маршруту. Вони містять
покажчик на маршрут struct sock -
> dst_cache * і викликають ip_route_connect (net/route.h) для знаходження маршруту інформація записується
в dst_cache і сокет далі
використовує її не повторюючи операції пошуку маршруту поки не станеться щось незвичайне
в цьому і є сенс connect p>
Встановлення
з'єднання p>
Розглянемо
стандартний приклад p>
/*
look up host */ p>
server = gethostbyname (SERVER_NAME); p>
/* get socket */ p>
sockfd = socket (AF_INET,
SOCK_STREAM, 0); p>
/* set up address */ p>
address.sin_family = AF_INET; p>
address.sin_port = htons (PORT_NUM); p>
memcpy (& address.sin_addr, server-> h_addr, server-> h_length); p>
/* connect to server */ p>
connect (sockfd, & address,
sizeof (address )); p>
socket створює
обєкти сокета певного типу і ініціалізує його також робить дефолтівських черги
(incoming, outgoing, error, backlog) і заголовок TCP p>
connect визначає
маршрути викликаючи протокольно залежні функції (tcp_v4_connect (), udp_connect ()) net/socket.c p>
asmlinkage long sys_connect (int fd,
struct sockaddr * uservaddr, int addrlen) p>
( p>
................................ p>
err = sock-> ops-> connect (sock,
(struct sockaddr *) address, addrlen, p>
sock-> file-> f_flags); p>
.......................... p>
) p>
int sock_create (int family, int
type, int protocol, struct socket ** res) p>
( p>
..................................... p>
// Створити протокольно залежний сокет! p>
//-------------------------------------- p>
if ((i =
net_families [family] -> create (sock, protocol)) <0) p>
( p>
sock_release (sock); p>
goto out; p>
) p>
................. p>
) p>
Опції
p>
Socket p>
Перевіряємо
помилки p>
Виділяємо
пам'ять p>
Ложім
сокет в список inode p>
Встановлюємо
покажчики на протокольно залежні частини p>
Зберігаємо
дані про тип і параметри сокета p>
Встановлюємо
сокет в положення закрито p>
Ініціалізіруем
черги пакетів p>
Connect p>
Перевіряємо
помилки p>
Визначаємо
Маршрут p>
Перевіряємо
кеш p>
Дивимося
в FIB p>
Створюємо
новий запис в таблиці маршрутизації p>
Заповнюємо
її і повертаємо p>
Зберігаємо
покажчик на запис маршрутизації в сокеті p>
Викликаємо
протокольно залежну функцію connect p>
Встановлюємо
сокет в сполучений p>
Також
треба не забути закрити сокет p>
Close викликає sock_close in socket.c p>
void sock_release (struct socket
* sock) p>
( p>
if (sock-> ops) p>
sock-> ops-> release (sock); p>
........................... p>
) p>
а
та через ланцюжок викликів протокольнозавісімую функцію p>
Додаткові функції p>
void inet_sock_release (struct sock
* sk) -net/ipv4/af_inet.c p>
назвніе
говорить за себе + хороший коментар Алана Коха p>
fib_lookup () - include/net/ip_fib.h p>
повертає
маршрут. Написана російською-Кузнецов! p>
fn_hach_lookup net/fib_hash.c p>
повертає маршрут за адресою p>
inet_create net/ipv4/af_inet.c p>
створює сокет p>
inet_release p>
ip_route_connect p>
викликає
ip_route_output для визначення адреси призначення p>
ip_route_output p>
ip_route_output_slow p>
rt_intern_hash
корисні для маршрутизації функції p>
sock_close () p>
sock_create () p>
sock_init_data net/core/sock.c ініціалізує основні поля сокета p>
sock_release net/socket.c p>
sys_socket p>
tcp_close net/ipv4/tcp.c p>
встановлює прапор FYN p>
tpc_connect net/ipv4/tpc_output.c p>
сохдает
пакети для з'єднання з встановленим розміром вікна p>
і
відповідними бітами, кладе пакет в чергу і виpивает p>
tcp_transmit_skb
щоб послати пакет p>
tcp_transmit_skb
-заповнює заголовок пакета і передає його p>
на уроветь IP p>
tcp_v4_connect () p>
викликає ip_route_connect p>
створює
з'єднувальний пакет і викликає tcp_connect p>
udp_close p>
udp_connect p>
Ця
частина описує процес обміну даними між різними рівнями ядра та мережі
Коли програма відправляє дані то воно пише в сокет той у своб чергу
визначає свій тип і викликає відповідну функцію, та передає дані
протоколу транспортного рівня (tcp, udp) функції етого рівня створюють структуру
sk_buff, копіюють в неї дані заповнюють заголовок свого рівня, вважають
контрольну суму і шлють на рівень IP.Там дописується заголовок
ip, checksum, можливо пакет фраг ментору і шле на xmit чергу мережевого
девайса, той посилає пакет у мережу. p>
dev_queue_xmit () - net/core/dev.c p>
spin_lock_bh ()
-блокуємо девайс p>
якщо
у нього є черга p>
calls
enqueue () додаємо пакет p>
calls
qdis () пробуджує девайс p>
else calls dev-> hard_start_xmit () p>
calls spin_unlock_bh () звільняємо девайс p>
DEVICE-> hard_start_xmit () - залежить від
девайса,
drivers/net/DEVICE.c p>
в
Загалом перевіряє відкрито чи пристрій p>
посилає
заголовок p>
говорить
системної шини послати пакет p>
оновлює статус p>
inet_sendmsg () - net/ipv4/af_inet.c p>
int inet_sendmsg (struct socket
* sock, struct msghdr * msg, int size, p>
struct scm_cookie * scm) p>
( p>
struct sock * sk = sock-> sk; p>
/* бінді сокет. */ p>
if (sk-> num == 0 & &
inet_autobind (sk)! = 0) p>
return
-EAGAIN; p>
викликаємо
функцію протоколу щоб послати дані p>
return sk-> prot-> sendmsg (sk,
msg, size); p>
) p>
ip_build_xmit - net/ipv4/ip_output.c
(604) p>
calls sock_alloc_send_skb () виділяємо пам'ять p>
= заголовочек = p>
if (! sk-> protinfo.af_inet.hdrincl)
( p>
iph-> version = 4; p>
iph-> ihl = 5; p>
iph-> tos = sk-> protinfo.af_inet.tos; p>
iph-> tot_len = htons (length); p>
iph-> frag_off = df; p>
iph-> ttl = sk-> protinfo.af_inet.mc_ttl; p>
ip_select_ident (iph,
& rt-> u.dst, sk); p>
if (rt-> rt_type! = RTN_MULTICAST) p>
iph-> ttl = sk-> protinfo.af_inet.ttl; p>
iph-> protocol = sk-> protocol; p>
iph-> saddr = rt-> rt_src; p>
iph-> daddr = rt-> rt_dst; p>
iph-> check = 0; p>
iph-> check =
ip_fast_csum ((unsigned char *) iph, iph-> ihl); p>
err = getfrag (frag, ((char
*) iph) + iph-> ihl * 4,0, length-iph-> ihl * 4); p>
) p>
calls
getfrag () копіюємо дані у юзера p>
returns rt-> u.dst.output () [=
dev_queue_xmit ()] p>
ip_queue_xmit () --
net/ipv4/ip_output.c (234) p>
Cмотри
маршрут p>
добудовує
ip заголовок p>
фрагментірум
якщо треба p>
adds IP checksum p>
calls skb-> dst-> output () [=
dev_queue_xmit ()] p>
qdisc_restart () --
net/sched/sch_generic.c (50) p>
вириває
пакет з черги p>
calls
dev-> hard_start_xmit () p>
оновлюємо
статистику p>
if
якщо помилка знову стввім пакет у чергу p>
sock_sendmsg () - net/socket.c (325) p>
перевіряємо права і таке інше p>
calls scm_sendmsg () [socket control
message] p>
шлемс дані p>
calls
sock-> ops [inet] -> sendmsg () and destroys scm p>
>>> sock_write () --
net/socket.c (399) p>
calls socki_lookup () accоцііруем сокету до inode p>
заповнюємо
заголовок повідомлення p>
returns
sock_sendmsg () p>
tcp_sendmsg () - net/ipv4/tcp.c (755) p>
ждемс з'єднання p>
skb = tcp_alloc_pskb пам'ять p>
calls csum_and_copy_from_user () робимо checksum & копіюємо p>
calls tcp_send_skb () p>
tcp_send_skb () --
net/ipv4/tcp_output.c (160) p>
це
головна routine посилки буфера p>
ми
ставимо буфер в чергу і вирішуємо залишити його там або послати p>
calls
__skb_queue_tail () додаємо в чергу p>
calls tcp_transmit_skb () якщо може p>
tcp_transmit_skb () --
net/ipv4/tcp_output.c (77) p>
будуємо заголовок tcp і чексумму p>
calls tcp_build_and_update_options () p>
перевіряємо ACKs, SYN p>
calls
tp-> af_specific [ip] -> queue_xmit () p>
udp_getfrag () - net/ipv4/udp.c p>
копіюємо
з адресного простору користувача і додаємо checksum p>
udp_sendmsg () - net/ipv4/udp.c p>
перевіряємо
прапори і тд p>
заповнюємо
заголовок p>
перевіряємо
мультікаст p>
заповнюємо
маршутних інформацію p>
calls
ip_build_xmit () p>
оновлюємо
статистику udp p>
returns
err p>
Отримання
даних p>
Отримання
даних починається з переривання від мережевої карти. Драйвер девайса виділяє
пам'ять і пересилає дані в той простір. Потім передає пакет у сполучний
рівень який викликає bottom-halv, яке обробляє подія поза
переривання пересилаючи дані на рівень вище-ip.Тот перевіряє помилки фрагменти,
маршрутизируют пакет або відсилає на рівень вище (tcp | | udp) Цей рівень
знову перевіряє помилки визначаютьсяет сокет якому призначений пакет і кладе його
в чергу сокета. Той у свою чергу будить користувальницький процес і копіює
дані в його буфер. p>
Читання
з сокета (1) p>
Намагаємося
щось прочитати (і засипаємо) p>
Заповнюємо
заголовок повідомлення дороговказом на буфер (сокет) p>
перевіряємо
прості помилки p>
передаємо
повідомлення inet сокет p>
Отримання
пакета p>
Пробудження
пристрої (переривання) p>
перевірка
девайса p>
Отримання
заголовка p>
виділення
пам'яті p>
ложім
пакет в те місце судячи з усього використовуючи DMA p>
ставимо
пакет в чергу p>
виставляємо
прапор запуску bottom-halv p>
BottomHalv p>
Запуск
мережевого ботом-халва p>
Пересилання
пакетів з девайса щоб не було переривань p>
пересилання
пакетів на рівень ip p>
очищення
черги відсилання p>
повернення p>
Рівень
IP p>
Перевірка
помилок p>
Дефрагментація
якщо необхідно p>
Визначення
маршруту (форвард чи ні) p>
Відсилання
пакету за призначенням (TCP | | UDP | | forwarding) p>
Отримання
пакету в UDP p>
Перевірка
помилок p>
перевірка
сокета призначення p>
пересилання
пакету в чергу сокета p>
пробудження
чекаючего процесу p>
Отримання
TCP p>
Перевірка
прапорів і помилок а також чи не був отриманий пакет раніше p>
Визначення
сокета p>
пересилання
пакету в чергу сокета p>
пробудження
чекаючего процесу p>
Читання
з сокета (2) p>
Пробудження
процесу p>
Виклик
соответствуюшей функції доставки (udp | | tcp) в буфер користувача p>
Повернення p>
IP
forwarding p>
Розглянемо
докладніше процес форвардинг пакетів p>
Спочатку
йде перевірка TTL і зменшення його на 1 Перевірка пакету на наявність дійсного
маршруту якщо такого немає то відсилається відповідне icmp повідомлення
копіювання пакету в новий буфер і звільнення старого Установка потрібних ip
опцій фрагменторованіе якщо необхідно відправка пакету на потрібний девайс p>
DEVICE_rx ()
девайсно залежна функція, p>
приклад
drivers/net/de600.c p>
тут
я спробую перевести чудові коментарі автора p>
Linux driver for the D-Link DE-600
Ethernet pocket adapter. P>
* p>
* Portions (C) Copyright 1993, 1994
by Bjorn Ekwall p>
* The Author may be reached as
[email protected] p>
/* p>
*
Якщо у нас хороший пакет то забираємо його з адаптера p>
*/ p>
static void p>
de600_rx_intr (struct net_device
* dev) p>
( p>
struct sk_buff * skb; p>
unsigned long flags; p>
int i; p>
int read_from; p>
int size; p>
register unsigned char * buffer; p>
save_flags (flags); p>
cli (); p>
/*
Визначаємо розмір пакету */ p>
size = de600_read_byte (RX_LEN, dev);
/ * Нижній байт */ p>
size + = (de600_read_byte (RX_LEN,
dev)
1535)) ( p>
printk ( "% s: Bogus packet size
% d.n ", dev-> name, size); p>
if (size> 10000) p>
adapter_init (dev); p>
return; p>
) p>
skb = dev_alloc_skb (size +2); p>
if (skb == NULL) ( p>
printk ( "% s: Couldn't allocate a
sk_buff of size% d.n ", p>
dev-> name, size); p>
return; p>
) p>
/* Інакше */ p>
skb-> dev = dev; p>
skb_reserve (skb, 2);/* Align */ p>
/*
'skb-> data' указивет на початок буфера
даних. */ p>
buffer = skb_put (skb, size); p>
/* копіюємо пакет у буфер */ p>
de600_setup_address (read_from,
RW_ADDR); p>
for (i = size; i> 0; - i,
+ + buffer) p>
* buffer = de600_read_byte (READ_DATA,
dev); p>
/* Визначаємо тип
протоколу p>
skb-> protocol = eth_type_trans (skb, dev); p>
/* Передаємо
на верхній рівень см net/core/dev.c p>
netif_rx (skb); p>
/*
оновлюємо статистику */ p>
dev-> last_rx = jiffies; p>
((struct net_device_stats
*) (dev-> priv)) -> rx_packets + +;/* кількість отримань */ p>
((struct net_device_stats
*) (dev-> priv)) -> rx_bytes + = size;/* кількість отриманих байт */ p>
/* p>
*
Якщо трапиться щось погане під час доставки, netif_rx () p>
*
зробило a mark_bh (INET_BH) для нас і буде працювати p>
*
коли ми ввійдемо в bottom-halv. p>
*/ p>
) p>
ip_finish_output ()
net/ipv4/ip_output p>
визначає
девайс для даного маршруту p>
викликає
функцію девайса [= dev_queue_xmit] p>
ip_forward -net/ipv4/ip_forward p>
в
це фото гарні коментарі p>
перевіряємо
роутер p>
якщо
пакет нікому не призначений то Дропан p>
якщо
поганий TTL аналогічно p>
якщо
неможет пакет отфорвардітся то відправляємо icmp пакет ICMP_DEST_UNREACH p>
якщо
необхідно шолом пакет ICMP HOST REDIRECT p>
копіюємо
і знищуємо старий пакет p>
зменшуємо
TTL p>
якщо
необхідно встановлюємо необхідні параметри ip_forward_options в p>
ip_forward_finish p>
функція отримання ip пакета p>
перевіряємо
помилки p>
погана
довжина p>
версія p>
чексумма p>
викликаємо
pskb_trim p>
викликаємо
ip_route_input p>
Процес
маршрутизації p>
Як
вже говорилося є тоюліца сусідів, FIB, routing cache Таблиця сусідів містить адреси (mac) комп'ютерів які фізично з'єднані
з нами. Linux використовує АRP для визначення адрес ета таблиця динамічна
хоча адміністратори можуть задати статичні записи. Стуктури пов'язані з етой
таблицею описані в include/net/neighbour.h основні структури. struct
neigh_table-їх цілий зв'язаний список struct neigh_parms-список містить
різноманітну статистику struct neighbour-hash таблиця сусідів асоційованих
з цією таблицею struct pneig_entry-hash всіх девайсів p>
поля struct neighbour p>
struct net_device-девайс p>
hh_cache
-вказівник на апаратний кеш p>
sk_buff_head
arp_queuq-чергу arp пакетів p>
є
local-в ній знаходяться свої інтерфейси p>
і
main в ній напевно все інше p>
Forwarding Information Database p>
struct fib_table в include/net/ip_fib.h p>
містить
покажчики на різні функції p>
tb_stamp p>
tb_id
-255 Для local і 254 для main p>
td_data-hash fib таблиця p>
struct fn_hash -net/ipv4/fib_hash.c p>
struct
fn_zone * fn_zones [33]-вказівники на зони p>
struct
fn_zone * fn_zone_list вказівник на перший не порожню зону p>
struct
fn_zone містить інформацію про зону і маршрути для неї p>
struct
fib_node ** fz_hash-вказує на кеш записів цієї зони p>
int
fz_nent кількість записів p>
int
fx_divisor числа Бакет для зони (в основному 16 крім зони 0000 p>
loopback
девайса) p>
int
fz_order індекс зони в батьківській fn_hash p>
struct
fib_node-містить інформацію по девайсу в fib_info (include/net/ip_fib.h) p>
метрику
, протокол і т.д p>
Routing
Cache p>
Це
найбільш швидкий спосіб знаходження маршруту Коли ip потрібний маршрут, то він
визначає комірку в хеше, що вказує на ланцюжок маршрутів і йде з цієї
ланцюжку поки не знайде потрібний маршрути мають таймери і частоту використання
, найбільш часті переміщуються по тексту. p>
struct
rtable-ланка в ланцюжку p>
містить
адреси відправника та одержувача p>
вхідний
інтерфейс p>
адреса
сусіда або шлюзу p>
struct
dst_entry p>
містить
спецефіческіх для даного маршруту характеристики та функції p>
struct
dev-зрозуміло p>
pmtu
максимальна довжина пакета для даного маршруту p>
int
(* input) (struct sk_buff)-вказівник на функцію прийому для даного маршруту p>
часто
ето tcp_rcv p>
int
(* output) (struct sk_buff) покажчик на функцію відсилання (dev_queue_xmit) p>
також
різноманітні статистичні дані та опції p>
Таким
чином нами було проведено дослідження мережної архітектури операційної
системи Линух на прикладі реалізації стека протоколів tcp-ip версії 4 в ядрі
2.4.7 p>
Додаток
p>
Після
тривалих теоретичних досліджень можна застосувати їх на практиці p>
Нашої
метою буде створення зручного для користувача інтерфейсу для зазначення в
пакеті підставного ip адреси (адреса якого немає у жодного нашого інтерфейсу)
Я не буду показувати, то як адреси виставляються в ядрі. Зауважу лише те що,
з сокета сімейства AF_INET і типу SOCK_RAW пакет з не своєю адресою відправити
начебто можна (в ядрі 2.2, насчет 2.4 невпевнений-може там є якісь
перевірки). сторінки мана говорять про опцію IP_HDRINCL. Їх можна відправляти
також через тип SOCK_PACKET. Але для всього цього знати код ядра не дуже
необхідно. Тому ми підемо іншим шляхом. p>
Найбільш
легкий шлях (?) зробити це через інтерфейс setsockopt. Після уважного
вивчення коду функції sys_setsockopt -net/socket.c знаходимо рядки if ((sock =
sockfd_lookup (fd, & err))! = NULL) p>
( p>
if (level == SOL_SOCKET) p>
err = sock_setsockopt (sock, level, optname, optval, optlen); p>
else p>
err = sock-> ops-> setsockopt (sock,
level, optname, optval, optlen); p>
sockfd_put (sock); p>
) p>
return err; p>
) p>
значить нам
треба шукати функцію setsockopt в коді для реалізації для типу sock_raw це файл net/ipv4/raw.c дивимося static int raw_setsockopt (struct sock * sk, int level, int optname, p>
char * optval, int optlen) p>
( p>
if (level! = SOL_RAW) p>
return ip_setsockopt (sk, level,
optname, optval, optlen); p>
................................... p>
) p>
ip_setsockopt лежить в net/ipv4/ip_sockglue.c в ній йде довгий перебір опцій
ми зупинимо свій вибір на рівні SOL_IP і додамо в перебір свої рядки/* HACK :>>>>>>>>>>>>>>>*/ p>
# ifdef CONFIG_HACKIP p>
case IP_HACKIP: p>
printk ( "HACKIP: setsockopt flag
% dn ", sk-> hackflag); p>
sk-> hackflag = 1; p>
get_user (val, (int *) optval); p>
printk ( "HACKIP: setsockopt val
% dn ", val); p>
sk-> hackf.src_addr = val; p>
break; p>
# endif p>
case IP_HDRINCL: p>
докладніше
опишемо відбуваються дії p>
printk
-виводимо отлабочние повідомлення p>
Я
не впевнений, але судячи з усього при створенні сокета вся структура обнуляється
тому ми можемо не дивитися прапор. Я додав цей рядок, щоб подивитися завжди
Чи він дорівнює 0 при не встановленій опції а після установки при повторі він дорівнює
1. get_user забираємо значення, подробиці include/asm/uaccess.h але для всього
цього нам треба додати відповідні поля у struct sock
======= sock.h ============= p>
......................... p>
# ifdef CONFIG_HACKIP p>
/* HACK :>>>>>>>>>>>>>>>>>>*/ p>
struct ip_hack ( p>
__u32 src_addr; p>
); p>
# endif p>
struct sock ( p>
/* Socket demultiplex comparisons on
incoming packets. */ p>
................................. p>
# ifdef CONFIG_HACKIP p>
/* HACK :>>>>>>>>>>>>>>>>>*/ p>
struct ip_hack hackf; p>
int hackflag; p>
# endif p>
........................................ p>
=========== end ====================== p>
тепер
нам треба перехопити відправку пакету p>
йдемо
у файл net/ipv4/ip_output.c і після всіх рядків де є 'iph-> saddr ='
вставляємо наш код # ifdef CONFIG_HACKIP p>
if ((sk-> hackf.src_addr! = 0) & & (sk-> hackflag == 1)) p>
( p>
iph-> saddr = sk-> hackf.src_addr; p>
printk ( "HACKIP: ip_build_and_send ..
% dn ", iph-> saddr); p>
) p>
# endif p>
Залишилось мале: у файл include/linux/in.h додаємо рядок # define IP_HACKIP 16 p>
в файл net/Config.in p>
bool 'HACKIP facilities'
CONFIG_HACKIP робимо p>
cd/usr/src/linux p>
make menuconfig p>
make dep p>
make bzImage p>
cp arh/i386/boot/bzImage
/ boot/kursach p>
правим lilo.conf або/boot/grub/menu.lst p>
соответствуюшая
команда p>
reboot .... p>
тепер
протестуємо нашу програму перепрошую за можливу наявність зайвих include
просто я переробив файл з друго-го проекту p>
============ rel.c ======================== p>
/* Written by Gleb Paharenko
2003 */ p>
/* Посвящяется
Кевин Митник */ p>
/* і
прекрасної весни в травні 2003-го */ p>
# include p>
# include p>
# include p>
# include p>
# include p>
# include p>
# include p>
# include p>
# include p>
# define IP_HACKIP 16 p>
int main () p>
( p>
int sd, res; p>
int value = 1; p>
int sval = 0; p>
int oval = 1; p>
char buffer [100]; p>
struct sockaddr_in addr, raddr; p>
bzero (buffer, sizeof (buffer )); p>
if ((sd = socket (PF_INET, SOCK_RAW, 6))