Media _RTP_ relaying
Document Sample


Подобряване производителността
на преноса на VoIP медия
(Решение на база оптимизации на
Linux ядро)
УВОД
С нарастване популярността и потреблението на телеко-
муникационни услуги в средата на Internet, се наложиха и качествено
нови подходи в разработката на средствата за тяхната реализация.
Огромна популряност добиха продукти за Internet телефония като
Skype, MSN и т.н., хардуерни решения във вид на VoIP gateways, мини-
и компактни АТА (аналогови телефонни адаптери), смесени телефонни
централи като Asterisk и OpenGK (като тези на Quintum, Cisco, Avaya и
т.н.) и други. Основна роля в цялото това многообразие от продукти
играят т.нар. протоколи за пренос на данни, в конкретния случай – за
пренос и контрол на гласови данни. Целия набор от програмни
средства и цялостни решения се базира на работата с въпросните
протоколи. Те се делят на два вида – протоколи за контрол и протоколи
за пренос. По настоящем съществуват и смесени разработки (протокол
в който са имплтементирани и контролната и преносната част – IAX2).
Обект на тази дипломна работа обаче са строго протоколите за пренос,
поради това че към настоящият момент обемат най-голям процент на
потребление в сферата на Internet телефонията.
Целта на настоящата разработка е оптмизирането преноса на
медия (гласови данни) през мрежовия стек на Linux ядрото, в случая
когато Linux се използва за т.нар. RTP proxy (софтуеър за препредаване
на медия и гласови данни). Същото може да бъде използвано като част
от по-мащабен проект за клъстеризиране на телефонна централа,
ползваща за сигнален пренос SIP или H323 протоколен стек (протоколи
SIP, H323, RTP, RTCP и SRTP). Решението се състои от специфично
допълнение към ядрото на Linux (kernel patch), както и подходящ
конфигурационен инструментариум във вид на потребителски
(userland) програмируем интерфейс (API).
2
ПЪРВА ГЛАВА
Съществуващи решения. Предимства и недостатъци.
Съществуват редица решения, реализиращи изисканата
функционалност. Общо взето начина им на работа може да се система-
тизира така – има три възможности за създаване на агент за
препредаване на медия трафик, без това да налага модификация на
Linux ядрото:
a. Потребителски медия агент (като този на PortaOne -
rtpproxy), където анализа на пакети и препредаване се
извършват в потребителското пространство (userspace)
чрез системните функции read()/write() и реализация на
NAT функционалност;
b. Подобен на [а] потребителски медия агент, при който
обаче, системните функции read()/write() са заменени от
sendfile() (която реализира т.нар. zerocopy (копиране от
входящ в изходящ буфер на два различни мрежови
интерфейса) в IP стека на Linux ядрото);
c. Управляващ софтуеър за netfilter (механизма в Linux
ядрото, реализиращ NAT, firewall, филтриране на пакети
и т.н.), реализиращ функционалността на предните две
решения, изключая препредаването (NAT), което се
реализира чрез запис на правила в NAT таблицата в Linux
ядрото.
Фигура 1 (по-горе) визуализира последния предложен подход,
наблягайки на това как медия трафика се препредава през Linux ядрото.
Когато във входните буфери на съответната мрежова карта пристигне
пакет, това е резултат от множество “извиквания” от периферия във
вид на изисквания за прекъсване или полинг (дори понякога в резултат
на директен достъп до паметта, ако това се поддържа от мрежовата
карта), в зависимост от текущата политика на диспечера на Linux
ядрото. Получения пакет се обработва от управляваща програма на
ниско ниво (device driver). В последствие пакета продължава пътя си
вече през мрежовия стек, като започва от втори слой (в случая –
Ethernet 802.3) и стига до функцията на IP стека ip_rcv(). Тук се прави
обработка на IP хедъра на съответния пакет – проверки за
консистенция и коректност (пакетна дължина, цялост, чек-суми и т.н.).
Ако обработката премине успешно, пакета достига netfilter prerouting
хуук-функциите (hook functions), където се извършва DNAT
3
(Destination NAT – промяна на данните в пакета, касаещи последвалото
рутиране и обработки на целевия контрол). След това, пакета минава
през своя “формален” път през кернела (dst_input()), netfilter
филтриращи хуук-функции, рутинг решение и накрая достига IP
функцията за изпращане. Тук пакета ре реасемблиран (ако това се
налага) и е пуснат за обработка от netfilter SNAT (Source NAT -
промяна на данните в пакета, касаещи изискването за транспарентност
на NAT от последвалите по пътя хостове) хуук-функциите. На финала,
пакета е пратен към изходните буфери на изходния мрежов интерфейс
чрез съответните функции за изпращане на ниско ниво.
Изглежда че това решение е технологично най-оптималното,
вземайки в предвид максималната продуктивност, постигана при
препращането на медия, както и релативната мрежова латентност
(първото като функция на второто). Мислейки по този начин обаче, се
изпускат някой важни съждения:
- IP стека в Linux ядрото е ОБЩО решение за обработка на
ВСИЧКИ видове IP пакети, които преминават през него;
- netfilter има същия недостатък като част от IP стека на
Linux ядрото.
По нататък ще се уверим че тези недостатъци са преодолими и
съществува възможност за създаване на далеч по-оптимизирано
решение.
4
фиг.1 - Път на медия трафика (RTP) в Linux ядрото
IP Stack
NETFILTER
IP decision
IP routing (LOCAL/
FILTER FWD)
<dst_input()>
(Firewall)
POSTRO PREROU
IP transmit UTING TING
IP receive
routine (SNAT) (DNAT) routine
<ip_finish_output
<ip_rcv()>
()>
Physical / Link Layer Stack
(PHY/LLC – dirvers and Ethernet stack)
Network Network
Interface Interface
B A
Network Network
Segment Segment
B A
(termination) (origination)
5
ВТОРА ГЛАВА
Оптимизирано решение. Общи положения.
2.1 Схема и описание на решението
Схемата по-долу показва, как слабостите, упоменати в предния
параграф могат да бъдат превъзмогнати. netfilter и почти всички
останали функции за приемане, предаване, препредаване, филтриране и
рутиране липсват. Тяхната функция е поета от rtpnat_process_skb(). Тя
релизира по-специалното третиране на UDP пакети,вземайки решения,
базирани на следната фактология:
- Всички UDP пакети, освен т.нар.
exception(изключителни) пакети, трябва да бъдат
обработени от RTPNAT;
- Оригиниращите и терминиращите IP логически
интерфейси НИКОГА не подлежат на промяна на
адресите си, освен чрез извършване на предварително
информиране на RTPNAT чрез специална функция;
- Всички останали пакети се пропускат до IP стека;
- netfilter е конфигуриран стриктно да използва различни
от RTPNAT портове на транпортното ниво, тъй като и
двете функционалности реализират NAT.
Така реализирания RTPNAT поради своята специфика (строго
специализирано решение на ниско ниво) постига производителност
повече от 400% в сравнение с най-оптималната производителност на
решенията от предходния параграф.
6
фиг.2 - Оптмизирано решение в Linux ядрото - RTPNAT
IP Stack
RTP IP receive
relaying routine
<rtpnat_process_
<ip_rcv()>
skb()>
Physical / Link Layer Stack
(PHY/LLC – dirvers and Ethernet stack)
Network Network
Interface Interface
B A
Network Network
Segment Segment
B A
(termination) (origination)
7
2.2 Използвани технологии
Операционна система – Linux (дистрибуцията не е от
значение за конкретното решение) с наличен сорс код на
ядрото. Употребата на тази ОС се налага от следните
съображения: разпространеност – втора след популярната
Microsoft Windows по потребление в света; отворен код –
позволява модификации на системно равнище; сигурност –
архитектурата й, както и набора от инструменти за
менажиране на мрежа и мрежови услуги са на много добро
ниво.
Език за програмиране – GNU C. Употребата на този език за
програмиране се налага по следните съображения – ядрото
на Linux е написано на С; най-близък по семантика,
синтаксис и идеология до Асембли (Assembly), като
резултата от това е оптимален код след компилация, както и
сравнително бърза обработка от страна на бекенда на
съответния компилатор (в случая GNU CC); най-удачен за
системни решения, изискващи едновременно бързина,
модулност и добра структура на кода.
Архитектура – IA32. Използва се по две основни причини:
цена – най-евтината възможна, в сравнение с PPC, MIPS и
т.н.; разпространение. Друга немаловажна причина е факта,
че Linux е проектирана за оптимална работа с IA32.
8
2.3 Използвани протоколи и стекове
2.3.1 TCP/IP
В основата на Интернет и пакетното предаване на данни стои
TCP/IP фамилията протоколи. Точно за това познаването и е от
първостепенна важност. Това е само едно кратко описание на TCP/IP
Интернет и на Internet Protocol (IP). В него са засегнати само основните
параметри на всеки протокол.
Transmission Control Protocol/Internet Protocol (TCP/IP) е софтуерно
базиран комуникационен протокол, използван в мрежите. Самото име
ни подсказва, че този продукт е съчетание от два протокола: TCP
(Transmission Control Protocol) и IP (Internet Protocol). Но терминът
TCP/IP не се отнася до просто съчетание на два протокола. Това е богат
набор от програми, които осигуряват мрежови услуги като: Remote
login, Remote file transfer, електронна поща и.т.н. TCP/IP обезпечава
метод за трансфер на информация от един компютър до друг.
Комуникационните протоколи следят за пътя, за достоверността на
доставените данни и за грешки при предаването. Те използват сигнали
за състоянието и по този начин имат действителен контрол върху
преноса по трасето. За всичко това се грижи TCP/IP. Когато използваме
TCP/IP, ние се обръщаме към един или няколко протокола от тази
фамилия, не само към TCP или IP.
За основен модел при мрежите се счита седем-слойният мрежови
модел (OSI). TCP/IP също е изграден на слоеве, но неговите слоеве не
съвпадат със слоевете на OSI модела.
фиг. 1
9
Възприемането на TCP/IP модела не влиза в противоречие с OSI
стандартите, защото двете са развивани съвместно. В някои случаи
TCP/IP допълва OSI и обратното. Съществуват някои важни разлики,
които произтичат от основните изисквания на TCP/IP. Някои от тях са:
динамичното рутиране, независими от физическата връзката протоколи
в мрежовия слой, универсална възможност за връзка и пакетна
комутация. Разликите между OSI архитектурата и тази на TCP/IP
засягат само слоевете над транспортното ниво и тези от мрежовия слой.
OSI има и двете: слоя сесия и слоя представяне, докато TCP комбинира
двата с потребителския слой. Необходимостта от
мултифункционалност на връзката налага TCP/IP да обедини двата
най-долни слоя (логическия и физическия). Имаме неоснователно
безпокойство относно комбинацията на мрежовите нива. Както се
вижда от фигурата, физическият и логическият слоеве са обединени в
един. Това може да бъде използвано от един интелигентен контролер,
например - мрежова платка. Комбинирането на двата слоя в един има
едно основно предимство. Това позволява една подмрежа да бъде
изградена без да се нуждае от други протоколи. Освен това дава
възможност на тези мрежи, използвайки TCP/IP протокола, да се
свържат и извън техните затворени системи.
Транспортният слой използва Transmission Control Protocol (TCP)
или някой друг протокол - най-често User Datagram Protocol (UDP). За
мрежовия слой има само един протокол - Internet Protocol (IP). Това
осигурява на системата универсална възможност за свързване.
TCP/IP е предпочитан от потребителите пред OSI модела. Някои от
причините за това са, че хиляди приложения използват TCP/IP и
неговия добре описан интерфейс с приложенията. Освен това TCP/IP
вече е доказал своята функционалност. Той е и база на повечето Unix
системи, които представляват голям дял от пазара на операционни
системи. Учудващо е, че американското правителство е един от най-
върлите противници на TCP/IP. Основният им аргумент е, че TCP/IP не
е международно възприет стандарт, докато OSI е такъв.
Сега ще направим един общ преглед на TCP/IP протоколите и
основните им функции.
10
Фиг. 2
Фигура 2 показва основните елементи на TCP/IP фамилията
протоколи. Можете да видите, че TCP/IP не е включен в най-долните
два слоя на OSI модела (физическия и логическия). TCP/IP започва от
мрежовия слой, където е разположен Internet Protocol (IP). В
транспортния слой се използват TCP и UDP протоколи. Над тях са
програмите и протоколите, които доизграждат TCP/IP фамилията. В
техните комуникационни системи те използват TCP или UDP и IP
слоевете. Фигура 2 показва също, че някои от горните слоеве зависят от
TCP (например Telnet и FTP), а други (TFTP и RPC) използват UDP.
Повечето от горните слоеве използват само един от двата транспортни
протокола, макар че някои включително DNS (Domain Name System)
могат да използват и двата. TCP/IP е зависим от концепцията clients -
server. Този термин има просто значение: всяко устройство, което
инициира връзката, е клиент, а устройството, което отговаря, е сървър.
Сървърът обслужва обслужва заявките на клиентите.
11
Бърз преглед на TCP/IP компонентите
За да се разбере ролята на многото компоненти на TCP/IP
фамилията протоколи, е полезно да се знае какво може да се прави в
една TCP/IP мрежа. Списъкът по-долу не е изчерпателен, но включва
основните потребителски приложения, които TCP/IP поддържа.
Telnet
Програмата Telnet дава възможност за отдалечен login. Това
позволява на даден потребител да се свърже с отдалечен компютър и да
му подава команди за изпълнение, сякаш работи на него. Връзката
може да се осъществи където и да е в локалната мрежа или в друга
такава където и да е по света. Достатъчно е потребителят да има
разрешение да се свърже с отдалечената система. Това се прави често в
LAN или WAN мрежи, но малко системи, достъпни чрез Интернет,
позволяват Telnet сесии.
File Transfer Protocol (FTP)
FTP позволява файлове от една система да се копират на друга.
Компютъра, към който потребителят се свързва, не става напълно
достъпен, както с Telnet. Тук се използва специален FTP софтуер.
Simple Mail Transfer Protocol (SMTP)
SMTP се използва за трансфер на електронна поща. SMTP е
напълно прозрачен за потребителя. SMTP се свързва към отдалечената
машина и прехвърля mail - съобщенията подобно на начина, по който
FTP прехвърля файлове. Работата на SMTP е почти незабележима и
това е един от протоколите, който дава най-малко грешки.
Kerberos
Kerberos е широко поддържан протокол за сигурността. Той
използва специално приложение, наречено authentication server за
валидиране на паролите и схемите на криптиране. Kerberos е една от
многото криптиращи системи за сигурност, използвани в
комуникациите и е често срещана в UNIX.
12
Domain Name System (DNS)
DNS позволява името на даден компютър да се преобразува в
определен мрежови адрес, например - PC, наречено "Genadi", не може
да бъде достъпен от друга машина в същата мрежа или от друга
свързана мрежа, ако не е налице някакъв метод за проверка на името на
локалното PC и заместване на името с хардуерния машинен адрес. DNS
осигурява преобразуване от локално име към уникалния физически
адрес на устройството, свързано в глобалната мрежа.
Simple Network Management Protocol (SNMP)
SNMP дава съобщения до администратора за състоянието и за
възникнали проблеми в мрежата. SNMP използва UDP като
транспортен механизъм. Тук терминологията е малко по-различна,
отколкото при TCP/IP. Вместо за клиенти и сървъри се говори за
"manager" и "agent", въпреки че се има предвид същото като при
TCP/IP. Един "agent" осигурява информация за едно устройство, а
"manager"-а комуникира през мрежата с много "agents".
Network File System (NFS)
NFS е набор от протоколи, разработен от "Sun Microsystems",
който осигурява на много компютри прозрачен достъп до техните
директории. Това е т. нар. разпределена файлова система. NFS
системите се използват често в големите корпоративни обкръжения.
Особено при тези, работещи под UNIX.
Remote Procedure Call (RPC)
RPC протоколът, съдържа набор от функции, които дават
възможност дадено приложение да комуникира с друг компютър
(сървър). Осигурява се разпределена обработка на данните (програмни
функции, връщане на кодове и предефиниране на променливи).
Trivial File Transfer Protocol (TFTP)
TFTP е много опростен протокол - FTP без защита. Той използва
UDP в транспортния слой.
13
Transmission Control Protocol (TCP)
TCP е част от TCP/IP фамилията. Той комуникационен протокол,
който осигурява надежден трансфер на данни. Отговорен е за
асемблирането на данните в стандартни пакети. Тези данни се подават
от приложения, работещи в по-горните слоеве. Освен това при него има
проверка за грешка.
User Datagram Protocol (UDP)
За разлика от TCP, UDP не осиурява повторно предаване на
"datagrams" при съобщение за грешка. UDP не е много надежден, но
той има по-специално предназначение. Ако приложението, което
използва UDP, има вградена надеждна проверка, бързодействието,
което получаваме, е за предпочитане.
Internet Protocol (IP)
IP отговаря за движението на пакетите с данни, асемблирани или
от TCP, или от UDP. Той използва уникален адрес за всяко устройство,
свързано в мрежата, и според този адрес определя маршрутите и
крайните точки.
Internet Control Message Protocol (ICMP)
ICMP е отговорен за проверката и генерира съобщения за статуса
на устройството в мержата. Той може да бъде използван, за да
информира други устройства дали има грешка в определена машина.
ICMP и IP обикновено работят заедно.
14
Интернет
Когато ARPANET израснала извън мащабите единствено на
военна мрежа и добавила към себе си други подмрежи
(университетски, корпоративни и потребителски), тя станала известна
като Интернет. Няма отделна мрежа, която да се нарича така. Терминът
се отнася към съвкупността от мрежи и подмрежи. Единственото нещо,
по което се приличат, е, че използват TCP/IP като комуникационен
протокол.
Структурата на Интернет
Както беше споменато по-рано, Интернет не е просто отделна
мрежа, а е съвкупност от мрежи, които комуникират помежду си чрез
"gateway". Той се дефинира като система, която извършва реални
функции между отделните мрежи (както е показано на фигурата).
Различните мрежи, свързани директно една с друга, или чрез "gateway",
често са наричани подмрежи (подчинени мрежи), защото те са само
малка част от голямата глобална мрежа. Това не означава, че дадена
подмрежа е малка или зависи от голямата мрежа. Подмрежите са
самостоятелни завършени системи.
Фиг. 3
15
С TCP/IP всички физически връзки между отделните мрежи се
осъществяват чрез "gateways". Важно е да се спомене, че "gateway"-ят
оказва пътя на информационните пакети като се базира на името на
мрежата, за която са предназначени. При него имаме обработка и
анализ на данните. Казано с други думи, задача на "gateway"-я е да
получи Protocol Data Unit (PDU) от глобалната мрежа или от друга
локална мрежа, да му окаже пътя към следващия "gateway" или да го
пропусне в локалната мрежа, за която е предназначен.
В САЩ съществува NFSNET, която е гръбнакът на Интернет (фигура
4). Едни от първите мрежи, свързани към NFSNET са: NASA - Space
Physics Analisys Network (SPAN), Computer Science Network (CSNET) и
някои други мрежи като WESTNET и San Diego Supercomputer Network
(SDSCNET). Има и други по-малки мрежи, които са потребителски
ориентирани. Това са BIINET, UUNET и др.
Фиг. 4
16
WESNET обхваща приблизително 3 000 изследователски сайта,
които са свързани чрез Т-3 линия, работеща на 44,736 Mbps.
Непрекъснато се правят тестове и нововъведения с цел да се увеличи
скоростта на гръбнака, да се подобри трафика, за да се задоволят
изискванията на нарастващия брой потребители. Някои от тестваните
технологии са: Synchronous Optical Network (SONET); Asynchronous
Transfer Mode (ATM) и др. Тези нови системи магат да оситурят
скорости над 1 Gbps.
Интернет слоеве
Повечето глобални мрежи, включително и Интернет, могат да се
разглеждат като мрежи със слойна структура. Концепцията със
слоевете помага при разработването на приложения за Интернет и
показва как различните нива на TCP/IP работят заедно. Но разделянето
на Интернет на слоеве е просто концептуално. Те не са действителни
физически или софтуерни нива, за разлика от OSI или TCP/IP. Удобно е
да приемем, че Интернет има 4 слоя.
Фиг. 5
17
Независимите устройства се намират в най-долния слой
(Subnetwork). Когато са свързани помежду си в локална мрежа, те се
превръщат в подмрежа. Над слоя Subnetwork е Internetwork. Тук се
осигурява комуникацията между подмрежите чрез "gateways". В този
втори слой (по възходящ ред) е мястото, където пакетите с данни се
прехвърлят от една мрежа към друга докато не достигнат дестинацията
си. Използваният протокол в този слой е IP.
Следващият слой - Service Provider Protocol, отговаря за пълната
комуникация по мрежата (end to end). В този слой работи TCP и други
протоколи. Той доставя данните и осигурява надеждност при
трансфера. Най-горният слой е Application service. В него се осигурява
връзката с потребителските приложения (електронна поща, файлов
трансфер, отдалечен достъп). В този слой се използват няколко
протокола.
Фиг. 6
На фигура 6 е показан пътя на даден пакет данни от едно
приложение до друго. Приложенията се намират в две отделни
подрежи. Слоевете в изпращащата и получаващата машина са OSI
слоеве. Отстрани са показани и еквивалентните им слоеве от Интернет
архитектурата.
18
Данните, генерирани от изпращащата машина, преминават от слой в
слой в низходяща посока. Datagram-ите се асемблират с помощта на
Protocol Control Information. Слоят Application добавя своя етикет към
данните и ги препраща в низходяща посока. Същото правят и слоевете
- Presentation, Session, Transport и т.н. Така datagram-ът увеличава своя
обем за сметка само на служебната информация. След като слоят
DataLink е добавил своя етикет към пакета с данните, той достига до
физическия слой и от него се изпраща извън локалната мрежа. LAN
мрежата маршрутизира информацията към "gateway"-я и през него тя
преминава в глобалната мрежа. По време на тези процеси LAN не се
интересува от съдържанието на съобщението. Някои мрежи променят
информацията в етикета, за да покажат, че пакета е преминал оттам.
Пакетът с данни преминава от "gateway" в "gateway" през глобалната
мрежа, докато накрая достигне подмрежата, за която е предназначен.
На всяка стъпка "gateway"-ят анализира хедъра на пакета с данни, за да
определи дали той е предназначен за дадената подмрежа. Ако да,
"gateway"-ят го пропуска. Ако не, "gateway"-ят оказва на данните
маршрута през глобалната мрежа. Този анализ се прави във физическия
слой. Това елиминира нуждата етикетите на всички смоеве да бъдат
прочитани. Така времето за обработка намалява и данните достигат до
дестинацията си с по-малко закъснение. Етикетът може да бъде
променян на всеки "gateway", за да се окаже пътят нататък.
Когато данните най-накрая пристигнат на "gateway"-я на подмрежата,
за където са предназначени, устройството разпознава, че данните са на
точното място и ги пропуска. Когато данните достигнат до
дестинацията си, те се изкачват нагоре по слоевете като протоколът от
всеки слой прочита своя етикет, отстранява го и пропуска данните
нагоре. Най-накрая в слоя Application даденият протокол прочита
хедъра, достига до истинското съобщение и го изпраща към нужното
приложение.
Интернет адреси
Мрежовите адреси са аналогични на адресите за електронна поща,
само че те оказват на дадена система къде да бъде доставен пакета с
данни. Най-често се използват три термина: име, адрес и маршрут.
Терминът адрес често се употребява много общо и може да означава
19
много различни неща: крайна точка, порт на дадения компютър, място
от паметта, приложение и т.н.
Името е специфичната идентификация на машината, на потребителя
или на приложението. Обикновено то е уникално и указва точното
местоположение.
Маршрутът се използва като термин, който оказва на системата как
пакетите с данни да достигнат до желания адрес. Често се използва
името на получателя. От името мрежовият софтуер, обръщайки се към
сървъра, се опитва да получи адреса и маршрута. Същото нещо се
получава и когато изпращата електронна поща. Там според името
пощенският сървър отнася съобщението до желаната пощенска кутия.
Съществуват няколко типа адресации в зависимост от платформата,
типа на мрежата и версията на софтеура.
Адресации в подмрежата
В мрежата са необходими два компонента, които да осигурят
коректно предаване на данните. Това са физическият адрес и адресът от
слоя DataLink.
Физически адреси
В една мрежа всяко устройство, което комуникира с другите, има
уникален физически адрес - хардуерен адрес. За хардуера адресите
обикновено са кодирани в мрежовата карта, реализирани или с ключове
или софтуерно. От гледна точка на OSI модела анализът за физически
адрес става във физическия слой. Ако адресът на получателя съвпада с
физическия адрес на устройството, Datagram-ът може да бъде предаден
към по-горните слоеве. Ако адресът не съвпада, Datagram-ът се
игнорира. Извършването на анализа в най-долния слой на OSI модела
предпазва от ненужни закъснения, защото в противен случай пакетът с
данни би трябвало да се изкачи до по-горен слой за анализ.
Дължината на физическия адрес е различна за различните мрежи. Но
Ethernet и някои други стандарти използват 48 бита за всеки адрес. За
да се осъществи комуникацията са необходими две адреса - един на
изпращащото и един на получаващото устройство. IEEE
20
(Международна организация за стандартизация) се грижи за
назначаването на универсални физически адреси за подмрежи. За всяко
подмрежа IEEE дава уникален идентификатор на организацията (OUI) с
дължина 24 бита. Дава се възможност на всяка организация да
асоциира още 24 бита към своя адрес, ако поиска.
Първите два бита са контролни. Първият бит е за индивидуален или
групов адрес. Ако е 0 - адресът е индивидуален. Ако е 1 - адресът е
групов и се нуждае от следващо уточняване. Вторият бит е локален или
универсален. Следващите 22 бита обслужват физическия адрес на
подмрежата и я идентифицират. Следващите 24 бита също
идентифицират локални мрежови адреси и се администрират локално.
Ако организацията изразходва физическите си адреси (за 24 бита те са
приблизително 16 млн.), IEEE има възможност да отпусне втори адрес
на подмрежата. Това са последните 24 бита.
Комбинацията от 24 бита от OUI и последните 24 бита, които са
локално идентифицирани, се нарича MAC адрес (Media Access Control).
Когато пакет с данни се асемблира, за да бъде пренесен по мрежата,
има два набора от MAC адреси: един набор от изпращащата машина и
един от получаващата.
Протоколът Address Resolution Protocol (ARP) - протокол за откриване
на адреси, е обслужващ за работата на протокола IP. Той осъществява
съпоставянето на IP адреса на мрежовия интерфейс с физическия MAC
адрес на този интерфейс. Всяка комуникация в рамките на един
физически сегмент на ниско ниво е свързана с МАС адресиране.
Преди комуникация с машина от собствения физически сегмент,
21
машината изпращач, използвайки протокола ARP, изпраща публично
съобщение, което достига до всички машини във физическия сегмент.
Съобщението съдържа IP адреса на мрежовия интерфейс на машината
получател, а също и IP и МАС адрес на машината инициираща
предаването. Машината получател след като открие пакета ARP е
длъжна да отговори на машината инициатор с пакет, съдържащ
собствения й МАС адрес. Машината инициатор, след като получи този
МАС адрес на машината приемник, може да започне предаването на
информация.
IP адреси
Протоколът IP използва понятието IP адрес. За всяка машина в
рамките на Интернет се задава уникален индентификатор, състоящ се
от 4 байта. IP адресите обикновено се представят с т.нар. десетична
точково нотация в следния вид: 192.34.10.17 Разпределението на IP
адресите между отделните компании и организации се извършва от
координиращи Интернет организации, както и от посредничещи
комуникационни компании.
Във връзка с наличието на маршрутизатори по мрежата,
осъществяващи физическото разделяне, а също и във връзка с
логическото сегментиране на мрежата, се въвежда понятието маска на
подмрежата. Това са 4 байта, но предназначени да дадат критерии дали
дадени две машини се намират в една и съща подмрежа. Маската на
подмрежата обикновено е еднаква за всички машини, участващи в тази
подмрежа.
Маската на подмрежата също се задава чрез точкова нотация в следния
вид: 255.255.255.0 Маската на подмрежата се съставя по такъв начин,
че след обръщането й в двоична бройна система тя съдържа единици
само в тези битови, които са еднакви за IP адресите на всички мрежови
интерфейси, участващи в подмрежата.
Например, имаме IP адрес на мрежова карта и той е 192.168.15.3. Нека
тази машина се намира в подмрежа с маска 255.255.255.0
192.168.15.3 = 11000000.10101000.00001111.00000011
255.255.255.0 = 11111111.11111111.11111111.00000000
Тогава в същата подмрежа се намират и адресите в интервала
22
192.168.15.0 и 192.168.15.255.
Адресите, завършващи на 0 и 255 имат специално предназначение в
TCP/IP и не се използват за IP адреси на машини. Практически
погледнато маската на подмрежата определя дали две машини могат да
обменят информация директно една с друга, или между тях ще се
намират посредничещи устройства - маршрутизатори.
Обикновено от компаниите, предоставящи IP адреси, се получава набор
от IP адреси. Ако в компанията, снабдила се с определен брой IP
адреси, има няколко разделени от маршрутизатори мрежи, тези адреси
трябва да бъдат разпределени за различните подмрежи.
Разграничаването на адресите от различните подмрежи отново става
като се използва маската на подмрежата, но няколко от нейните битове
се използват, за да пригодят работата на IP към вътрешното за
компанията сегментиране. Маската на подмрежата разделя адреса на
две части: едната от тях се нарича номер на подмрежата, а другата -
адрес на хоста. Дефинират се три основни класа IP адреси, достъпни
като валидни номера на мрежови интерфейси.
Адресните пространства
10.0.0.0 - 10.255.255.255
172.16.0.0 - 172.31.255.255
192.168.0.0 - 192.168.255.255
са заделени за частни мрежи, които няма да бъдат свързани към
Интернет.
Адресът 127.0.0.1 винаги сочи към локалната машина. Той се нарича
адрес за обратна връзка (Loopback address).
Ако мрежата на компанията съдържа например 5 подмрежи, то те
могат да бъдат различени една от друга, като се използват 3 бита (23=8)
от подмрежата, така тя ще бъде отбелязана с маска на подмрежата
например 255.255.255.224.
255.255.255.224 = 11111111.11111111.11111111.11100000
Създавайки такава маска на подмрежа, ограничаваме броя на битовете
в IP адресите, които могат да бъдат различни. Така 254 адреса,
23
намиращи се в една единствена подмрежа с маска 255.255.255.0, ние
създаваме критерий, изискващ не само първите три октета да са
еднакви, но и първите три бита от последния октет също да бъдат
еднакви. Това ни дава право да варираме само последните 5 бита. Чрез
тяхната промяна получаваме адресите на отделните компютри в дадена
подмрежа. Те вече не са 254, а могат да бъдат само 32.
С други думи, поставяйки маската 255.255.255.224, ние отделихме
общо 8 подмрежи, всяка от които съдържа по 32 уникални IP адреса.
На практика обаче в горния случай разполагаме с 6 подмрежи и 30
адреса във всяка от тях. Както адресът на мрежата, така и адресът на
мрежовия интерфейс не трябва да се състоят само от единици или само
от нули, тъй като тези адреси имат специално предназначение.
От друга страна броят на подмрежите винаги е равен на степента на
числото 2, намален с 2, защото има два случая на невалидни номера на
подмрежи - състоящи се само от единици и състоящи се само от нули.
В крайна сметка, разполагайки с набор от адреси от 192.168.15.1 до
192.168.15.254 с маска 255.255.255.224, ние го разделихмена следните
подмрежи и адреси, използвайки маската 255.255.255.224
Комуникацията между различните физически адреси се
осъществява посредством т.нар. маршрутизатори. Когато един
компютър, използвайки собствената си маска на подмрежа, открие, че
компютърът, на който ще предава информация, има различен адрес на
подмрежа от неговия (т.е. намира се в друга подмрежа), той предава
пакета към маршрутизатора, който се опитва да осъществи пренасянето
на информацията към отдалечения компютър.
24
2.3.2 Session Initation Protocol
В общи линии, Session Initiation Protocol (SIP) е протокол за пренос
на глас по IP (VoIP) с отворена архитектура, разработен от IETF
MUSIC Working Group. Той намира много широко приложение –
ползвайки го, бихте могли да се чуете с някой познат, който живее в
същия град, или пък да изградите мащабна телекомуникационна
мрежа, простираща се в няколко държави.
Няколко примера за употребата на SIP протокола:
IP-към-IP разговор със SIP
Когато общувате с някои познат, който също разполага с
компютър и връзка с интернет, посредством Skype, вашият разговор се
рутира изцяло по интернет, без да се използва традиционната PSTN
телефонна инфраструктура. В най-елементарния си вид SIP може да
бъде използван именно за такива разговори, които се рутират изцяло по
световната мрежа, както е и при Skype, Google Talk и т.н.
25
SIP базирана IP-към-PSTN телефония
При VoIP операторите, като Skype, тази функционалност е налице
посредством закупуването на SkypeOut ваучер (средната цена за
разговор чрез SkypeOut е около $0,023/минута), като за потребителите
на Sкype практически алтернатива няма.
При SIP обаче това не е така: благодарение на SIP ще имате
опцията да избирате измежду стотици телекомуникационни компании.
Например ако се абонирате за услугите на VoIP доставчика Voxee, ще
можете да осъществите връзка от компютъра си до произволен
стационарен телефон, при това на около два пъти по-ниска цена,
отколкото чрез SkypeOut – средно $0,011/минута.
SIP-базирана PSTN-към-IP телефония
Повечето доставчици на SIP VoIP услуги предлагат и обратната
възможност, т.е. за осъществяване на връзка от стационарен телефон
към IP срещу малка месечна такса, която обикновено е под $5 месечно,
получавате собствен телефонен номер. Когато някой се обади на този
номер, разговорът се рутира в интернет посредством SIP, сякаш
връзката се осъществява директно от IP-към-IP. Този тип услуги се
наричат DID (Direct Inward Dial).
26
SIP телефони
При Skype, освен от интернет свързаност, потребителят
задължително се нуждае и от компютър, където да бъде инсталиран
софтуерът. При SIP това не е задължително условие, защото бихте
могли да използвате хардуерен SIP телефон, който има само един RJ-45
Ethernet жак, без изобщо да се нуждаете от компютър. Качествени
хардуерни SIP телефонни апарати могат да се намерят за около $80. На
пазара от известно време се предлагат и безжични, хардуерни SIP
базирани устройства, където се разчита на 802.11b Wifi вместо на
Ethernet свързаност.
Естествено съществуват и софтуерни телефони, ползващи SIP,
например OpenWengo, а се очаква, че и Google Talk в скоро време ще
поддържа SIP.
Аналогов SIP телефонен адаптер
При положение че желаете да запазите сегашния си аналогов
телефонен апарат, но бихте искали да се възползвате от опциите, които
SIP VoIP телефонията предлага, ще трябва да се снабдите с т.нар. SIP
Analog Telephone Adapter (ATA) адаптер. Цената на този тип
устройства е около $100. Те са снабдени със стандартен RJ-45 Ethernet
порт и RJ-11 телефонна букса. Просто включвате телефонния си апарат
и UTP кабела в ATA адаптера, насочвате го към избрания от вас SIP
27
VoIP доставчик, и вече разполагате с директна, SIP базирана VoIP
свързаност, като за целта отново изобщо не се изисква компютър.
28
ТРЕТА ГЛАВА
Оптимизирано решение. Реализация.
3.1. Описание на функциите, общ програмируем интерфейс
(API) и структури от данни
- Структури от данни
struct pre_nat_entry
{
unsigned int source_ip;
unsigned short source_port;
unsigned short assoc_port;
unsigned int dest_ip;
}
Тази структура се използва от потребителска програма за
конфигурация на RTPNAT регистрационен пакет. Също така се
използва и от вътрешно-сесийната база данни за менажиране на
некуплирани NAT регистрационни пакети;
struct session_entry
{
char session_id[256];
char session_len;
struct pre_nat_entry nat;
}
Тази структура реализира конфигурационен примитив на RTP
сесия в ядрото.
struct nat_entry
{
/* Source IP address of the incoming packet */
unsigned int source_ip;
/* Source port */
unsigned short source_port;
/* Associated NAT port */
unsigned short assoc_port;
/* Link to the other end of the pair (by port number as
an index) */
unsigned short link;
/* NAT entry is active. */
unsigned int active;
/* Last time, when this entry was used (in system ticks
/ jiffies) */
unsigned long last;
}
Тази структура служи за запазване на регистраионните пакети и
релативната към тях информация в ядрото.
29
struct ll_entry
{
void *data;
struct ll_entry *prev;
struct ll_entry *next;
};
struct ll_list
{
struct ll_entry *head, *tail;
};
Тези структури реализират вътрешните бази данни чрез
едносвързани списъци..
- Статични методи
static unsigned short update_nat_table (struct pre_nat_entry
*nat);
Създава неактивен NAT регистрационен пакет в RTP NAT
таблицата. Пакета е неактивен, защото все още не е куплиран с
друг такъв;
static void activate_nat_pair (struct pre_nat_entry
*nat1, struct
pre_nat_entry *nat2);
Активира и куплира регистрационни пакети;
static unsigned int extract_entity (unsigned int ip);
Вътрешна функция за индексация на NAT таблица, на база IP
адрес;
static unsigned short get_port (unsigned int entity);
Връща свободен UDP порт от пула със неупотребени UDP
портове;
static void check_expired (void);
Проверява за NAT куплирани регистрационни пакети, вземайки
в предвид че са с изтекла годност (пакета е неупотребен за
повече от 5 секунди) или че техния статус е неактивен
(NAT_INACTIVE).
- Вътрешен програмируем интерфейс (API) на ядрото
unsigned int rtpnat_set_ent (unsigned int ent_ip1, unsigned int
ent_ip2)
Конфигурира RTPNAT модула с IP адреси на оригиниращия и
терминиращия интерфейси;
30
void rtpnat_flush (void)
Подготвя RTPNAT модула за инициализация;
unsigned short rtpnat_session_registrar (struct session_entry
*sentry)
Регистрира NAT сесия;
int rtpnat_process_skb (struct sk_buff *skb, struct net_device
*dev)
Процесира UDP пакети. Използвана от ip_rcv();
void __init rtpnat_init(void)
Инициализира RTPNAT модула. Викана еднократно при
инициализация на IP стека в Linux ядрото.
3.2. Потребителски интефейс и примери
- Инициализиране на комуникационен сокет
RTPNAT модула се конфигурира чрез системната функция
getsockopt(). С тази цел се създава сокет тип STREAM и в
последствие конфигурационната инормация се “инжектира” в
ядрото чрез горепосочената функция, приложена на въпросния
сокет.
Пример:
int sk;
……..
if (-1 == (sk = socket (PF_INET, SOCK_STREAM, 0)))
/* We have error */
- Конфигурация на оригиниращия и терминиращия
интерфейси
Пример:
int sk;
unsigned int ips[2];
socklen_t len;
……..
ips[0] = 0xc0a80201; // 192.168.2.1 in host order
ips[1] = 0xc0a80102; // 192.168.1.2 in host order
len = 2 * sizeof (int);
if (-1 == getsockopt (sk, IPPROTO_IP, IP_RTP_NAT_SET_DATA, (void *)ips, &len))
fprintf (stderr, "%s\n", strerror(errno));
31
- Преинициализация на оригиниращия и терминиращия
интерфейси
Пример:
int sk;
unsigned int cmd;
socklen_t len;
……..
cmd = IP_RTP_NAT_FLUSH;
len = sizeof(int);
if (-1 == getsockopt (sk, IPPROTO_IP, IP_RTP_NAT_SET_DATA, (void *)&cmd,
&len))
fprintf (stderr, "%s\n", strerror(errno));
- Установяване на сесия
Пример:
int sk;
struct session_entry session;
int i;
socklen_t len;
………
len = sizeof (session);
sprintf (session.session_id, "blaboza%d", i);
session.session_len = strlen (session.session_id);
session.nat.dest_ip = ips[0];
session.nat.source_ip = 0xc0a80103;
session.nat.source_port = 2345;
if (-1 == getsockopt (sk, IPPROTO_IP, IP_RTP_NAT_SET_DATA, (void *)&session,
&len))
fprintf (stderr, "TEST2: %s\n", strerror(errno));
else
printf ("TEST2: Returned pair - %8.8x:%u\n", session.nat.dest_ip,
session.nat.assoc_port);
len = sizeof (session);
sprintf (session.session_id, "blaboza%d", i);
session.session_len = strlen (session.session_id);
session.nat.dest_ip = ips[1];
session.nat.source_ip = 0xc0a80202;
session.nat.source_port = 2345;
if (-1 == getsockopt (sk, IPPROTO_IP, IP_RTP_NAT_SET_DATA, (void *)&session,
&len))
fprintf (stderr, "TEST2: %s\n", strerror(errno));
else
printf ("TEST2: Returned pair - %8.8x:%u\n", session.nat.dest_ip,
session.nat.assoc_port);
32
ЧЕТВЪРТА ГЛАВА
Ръководство на потребителя.
За по-голяма яснота, а и от практическа гледна точка, ще
разгледаждаме предложеното от нас решение като част от доста по-
комплицирана система – Call Center.
Фиг.3 – Цялостно решение с участие на RTPNAT
33
4.1 Call center – общи положения.
Call center е услуга, която позволява на множество потребители да
ползват определен набор от интерактивни услугив единна система.
4.2 Инсталация и конфигурация
Тъй като изграждането на Call center не е предмет на настоящата
дипломна работа (а се дава като пример за цялостно решение на база
RTPNAT), ще разгледаме инсталацията и конфигурацията на системата
за медиен релей (нашето решение RTPNAT).
По отношение на хардуеъра е добре системата да е достатъчно
мощна, за да може да препредаде достатъчно голям брой паралелни
медийни сесии за единица време. В противен случай решението би
било неприложимо от гледна точка на рентабилност. Предложената
система е следната – 2xCPU Pentium4 Xeon HT / 3.4 GHz; 2 GB DDR
RAM / 800 MHz; достатъчно добра дънна платка, реализираща шина
за достъп до паметта на честота 800 MHz; хард диск, видео карта и
др. параметри не са от значение; мрежова карта – препоръчително е
мрежовата карта да е поне гигабитов модел на Intel с 2 гигабитови
порта, реализираща NAPI (възможност за работа в полинг режим).
По отношение на софтуеъра – стандартна инсталация на Linux в
зависимост от дистрибуцията. При Slackware 10.1: инсталация чрез
bootable CDs – установяване на инсталационния диск в CD-ROM
устройството, избиране на стандартно буутване на Linux, влизане
като потребител root (супер потребител; при инсталация не се
изисква парола); стартиране на програмата за инсталация setup;
задаване на устройство за инсталация и на устройство-инсталатор;
създаване на подходящия набор от дялове на диска; изискване за
инсталация на Linux ядро, прилежащ мрежов стек и необходими
мрежови и терминални услуги; конфигурация на терминалните и
мрежовите услуги; конфигурация на boot loader (LILO – Linux
Loader) за съответния дял или глобално (в MBR – Master Boot
Record) за съответния мастер диск; приключване на основната
инсталация.
Прилагане на решението и конфигурация. Инсталация на Linux ядро
2.6.14 (препоръчителна версия); прилагане на съответния патч на
решението към ядрото; активиране на нововъведената опция
RTPNAT в Network Options; прекомпилация на ядрото; компилация
на userland приложението за управление.
34
4.3 Бележки и ограничения
- Този модул е предвиден само и само за Linux ядро версия 2.6.14 и
нагоре;
- Промяна IP адресите на оригиниращия и терминиращия
интерфейси по начин различен от описаните по-горе, би довела до
непредвидими последствия за цялата система;
- Използването на RTPNAT и Netfilter NAT (iptables) не е
препоръчително поради факта, че двете решения достъпват един и
същи ресурс.
35
ЗАКЛЮЧЕНИЕ
Разгледаната тук разработка е опит за подобряване на готово като
идеология, но недовършено в структурно отношение решение. Също
така, предложената технология е само модул, чието самостоятелно
ползване няма смисъл – той трябва да е част или от Call Center, или от
голям медиен клъстър или от стравнително голяма система,
реализираща телефонна централа.
Разбира се, разработката не е оптимизирана до край (което само по
себе си е невъзможно) и е възможно да се подобри в някой отношения
– избягването на рутинг и netfilter функционалността в ядрото, слизане
на по-долно ниво в мрежовия стек (Ethernet), генериране на детайлни
статистики от за създадените канали на препредаване и т.н. Но от друга
страна това е едно добро решение, имащо производителност с над
400% по-добра от алтернативна подобна система. Естествено
разработката винаги може да се развива още, но смятам че
достигнатото ниво е задоволително от гледна точка на поставените
цели.
36
ПРИЛОЖЕНИЯ
Сорс код на решението
1. linux/net/ipv4/Kconfig
#
# IP configuration
#
config IP_RTP_NAT
bool "IP: RTP proxy"
depends on INET
help
Kernel-level media (RTP) proxy. Read documentation for details.
config IP_MULTICAST
bool "IP: multicasting"
depends on INET
help
This is code for addressing several networked computers at once,
enlarging your kernel by about 2 KB. You need multicasting if you
intend to participate in the MBONE, a high bandwidth network on top
of the Internet which carries audio and video broadcasts. More
information about the MBONE is on the WWW at
<http://www-itg.lbl.gov/mbone/>. Information about the multicast
capabilities of the various network cards is contained in
<file:Documentation/networking/multicast.txt>. For most people, it's
safe to say N.
config IP_ADVANCED_ROUTER
bool "IP: advanced router"
depends on INET
---help---
If you intend to run your Linux box mostly as a router, i.e. as a
computer that forwards and redistributes network packets, say Y; you
will then be presented with several options that allow more precise
control about the routing process.
The answer to this question won't directly affect the kernel:
answering N will just cause the configurator to skip all the
questions about advanced routing.
Note that your box can only act as a router if you enable IP
forwarding in your kernel; you can do that by saying Y to "/proc
37
file system support" and "Sysctl support" below and executing the
line
echo "1" > /proc/sys/net/ipv4/ip_forward
at boot time after the /proc file system has been mounted.
If you turn on IP forwarding, you will also get the rp_filter, which
automatically rejects incoming packets if the routing table entry
for their source address doesn't match the network interface they're
arriving on. This has security advantages because it prevents the
so-called IP spoofing, however it can pose problems if you use
asymmetric routing (packets from you to a host take a different path
than packets from that host to you) or if you operate a non-routing
host which has several IP addresses on different interfaces. To turn
rp_filter off use:
echo 0 > /proc/sys/net/ipv4/conf/<device>/rp_filter
or
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
If unsure, say N here.
config IP_MULTIPLE_TABLES
bool "IP: policy routing"
depends on IP_ADVANCED_ROUTER
---help---
Normally, a router decides what to do with a received packet based
solely on the packet's final destination address. If you say Y here,
the Linux router will also be able to take the packet's source
address into account. Furthermore, the TOS (Type-Of-Service) field
of the packet can be used for routing decisions as well.
If you are interested in this, please see the preliminary
documentation at <http://www.compendium.com.ar/policy-routing.txt>
and <ftp://post.tepkom.ru/pub/vol2/Linux/docs/advanced-routing.tex>.
You will need supporting software from
<ftp://ftp.tux.org/pub/net/ip-routing/>.
If unsure, say N.
config IP_ROUTE_FWMARK
bool "IP: use netfilter MARK value as routing key"
depends on IP_MULTIPLE_TABLES && NETFILTER
help
If you say Y here, you will be able to specify different routes for
38
packets with different mark values (see iptables(8), MARK target).
config IP_ROUTE_MULTIPATH
bool "IP: equal cost multipath"
depends on IP_ADVANCED_ROUTER
help
Normally, the routing tables specify a single action to be taken in
a deterministic manner for a given packet. If you say Y here
however, it becomes possible to attach several actions to a packet
pattern, in effect specifying several alternative paths to travel
for those packets. The router considers all these paths to be of
equal "cost" and chooses one of them in a non-deterministic fashion
if a matching packet arrives.
config IP_ROUTE_VERBOSE
bool "IP: verbose route monitoring"
depends on IP_ADVANCED_ROUTER
help
If you say Y here, which is recommended, then the kernel will print
verbose messages regarding the routing, for example warnings about
received packets which look strange and could be evidence of an
attack or a misconfigured system somewhere. The information is
handled by the klogd daemon which is responsible for kernel messages
("man klogd").
config IP_PNP
bool "IP: kernel level autoconfiguration"
depends on INET
help
This enables automatic configuration of IP addresses of devices and
of the routing table during kernel boot, based on either information
supplied on the kernel command line or by BOOTP or RARP protocols.
You need to say Y only for diskless machines requiring network
access to boot (in which case you want to say Y to "Root file system
on NFS" as well), because all other machines configure the network
in their startup scripts.
config IP_PNP_DHCP
bool "IP: DHCP support"
depends on IP_PNP
---help---
If you want your Linux box to mount its whole root file system (the
one containing the directory /) from some other computer over the
net via NFS and you want the IP address of your computer to be
discovered automatically at boot time using the DHCP protocol (a
special protocol designed for doing this job), say Y here. In case
39
the boot ROM of your network card was designed for booting Linux and
does DHCP itself, providing all necessary information on the kernel
command line, you can say N here.
If unsure, say Y. Note that if you want to use DHCP, a DHCP server
must be operating on your network. Read
<file:Documentation/nfsroot.txt> for details.
config IP_PNP_BOOTP
bool "IP: BOOTP support"
depends on IP_PNP
---help---
If you want your Linux box to mount its whole root file system (the
one containing the directory /) from some other computer over the
net via NFS and you want the IP address of your computer to be
discovered automatically at boot time using the BOOTP protocol (a
special protocol designed for doing this job), say Y here. In case
the boot ROM of your network card was designed for booting Linux and
does BOOTP itself, providing all necessary information on the kernel
command line, you can say N here. If unsure, say Y. Note that if you
want to use BOOTP, a BOOTP server must be operating on your network.
Read <file:Documentation/nfsroot.txt> for details.
config IP_PNP_RARP
bool "IP: RARP support"
depends on IP_PNP
help
If you want your Linux box to mount its whole root file system (the
one containing the directory /) from some other computer over the
net via NFS and you want the IP address of your computer to be
discovered automatically at boot time using the RARP protocol (an
older protocol which is being obsoleted by BOOTP and DHCP), say Y
here. Note that if you want to use RARP, a RARP server must be
operating on your network. Read <file:Documentation/nfsroot.txt> for
details.
# not yet ready..
# bool ' IP: ARP support' CONFIG_IP_PNP_ARP
config NET_IPIP
tristate "IP: tunneling"
depends on INET
select INET_TUNNEL
---help---
Tunneling means encapsulating data of one protocol type within
another protocol and sending it over a channel that understands the
encapsulating protocol. This particular tunneling driver implements
40
encapsulation of IP within IP, which sounds kind of pointless, but
can be useful if you want to make your (or some other) machine
appear on a different network than it physically is, or to use
mobile-IP facilities (allowing laptops to seamlessly move between
networks without changing their IP addresses).
Saying Y to this option will produce two modules ( = code which can
be inserted in and removed from the running kernel whenever you
want). Most people won't need this and can say N.
config NET_IPGRE
tristate "IP: GRE tunnels over IP"
depends on INET
select XFRM
help
Tunneling means encapsulating data of one protocol type within
another protocol and sending it over a channel that understands the
encapsulating protocol. This particular tunneling driver implements
GRE (Generic Routing Encapsulation) and at this time allows
encapsulating of IPv4 or IPv6 over existing IPv4 infrastructure.
This driver is useful if the other endpoint is a Cisco router: Cisco
likes GRE much better than the other Linux tunneling driver ("IP
tunneling" above). In addition, GRE allows multicast redistribution
through the tunnel.
config NET_IPGRE_BROADCAST
bool "IP: broadcast GRE over IP"
depends on IP_MULTICAST && NET_IPGRE
help
One application of GRE/IP is to construct a broadcast WAN (Wide Area
Network), which looks like a normal Ethernet LAN (Local Area
Network), but can be distributed all over the Internet. If you want
to do that, say Y here and to "IP multicast routing" below.
config IP_MROUTE
bool "IP: multicast routing"
depends on IP_MULTICAST
help
This is used if you want your machine to act as a router for IP
packets that have several destination addresses. It is needed on the
MBONE, a high bandwidth network on top of the Internet which carries
audio and video broadcasts. In order to do that, you would most
likely run the program mrouted. Information about the multicast
capabilities of the various network cards is contained in
<file:Documentation/networking/multicast.txt>. If you haven't heard
about it, you don't need it.
41
config IP_PIMSM_V1
bool "IP: PIM-SM version 1 support"
depends on IP_MROUTE
help
Kernel side support for Sparse Mode PIM (Protocol Independent
Multicast) version 1. This multicast routing protocol is used widely
because Cisco supports it. You need special software to use it
(pimd-v1). Please see <http://netweb.usc.edu/pim/> for more
information about PIM.
Say Y if you want to use PIM-SM v1. Note that you can say N here if
you just want to use Dense Mode PIM.
config IP_PIMSM_V2
bool "IP: PIM-SM version 2 support"
depends on IP_MROUTE
help
Kernel side support for Sparse Mode PIM version 2. In order to use
this, you need an experimental routing daemon supporting it (pimd or
gated-5). This routing protocol is not used widely, so say N unless
you want to play with it.
config ARPD
bool "IP: ARP daemon support (EXPERIMENTAL)"
depends on INET && EXPERIMENTAL
---help---
Normally, the kernel maintains an internal cache which maps IP
addresses to hardware addresses on the local network, so that
Ethernet/Token Ring/ etc. frames are sent to the proper address on
the physical networking layer. For small networks having a few
hundred directly connected hosts or less, keeping this address
resolution (ARP) cache inside the kernel works well. However,
maintaining an internal ARP cache does not work well for very large
switched networks, and will use a lot of kernel memory if TCP/IP
connections are made to many machines on the network.
If you say Y here, the kernel's internal ARP cache will never grow
to more than 256 entries (the oldest entries are expired in a LIFO
manner) and communication will be attempted with the user space ARP
daemon arpd. Arpd then answers the address resolution request either
from its own cache or by asking the net.
This code is experimental and also obsolete. If you want to use it,
you need to find a version of the daemon arpd on the net somewhere,
and you should also say Y to "Kernel/User network link driver",
42
below. If unsure, say N.
config SYN_COOKIES
bool "IP: TCP syncookie support (disabled per default)"
depends on INET
---help---
Normal TCP/IP networking is open to an attack known as "SYN
flooding". This denial-of-service attack prevents legitimate remote
users from being able to connect to your computer during an ongoing
attack and requires very little work from the attacker, who can
operate from anywhere on the Internet.
SYN cookies provide protection against this type of attack. If you
say Y here, the TCP/IP stack will use a cryptographic challenge
protocol known as "SYN cookies" to enable legitimate users to
continue to connect, even when your machine is under attack. There
is no need for the legitimate users to change their TCP/IP software;
SYN cookies work transparently to them. For technical information
about SYN cookies, check out <http://cr.yp.to/syncookies.html>.
If you are SYN flooded, the source address reported by the kernel is
likely to have been forged by the attacker; it is only reported as
an aid in tracing the packets to their actual source and should not
be taken as absolute truth.
SYN cookies may prevent correct error reporting on clients when the
server is really overloaded. If this happens frequently better turn
them off.
If you say Y here, note that SYN cookies aren't enabled by default;
you can enable them by saying Y to "/proc file system support" and
"Sysctl support" below and executing the command
echo 1 >/proc/sys/net/ipv4/tcp_syncookies
at boot time after the /proc file system has been mounted.
If unsure, say N.
config INET_AH
tristate "IP: AH transformation"
select XFRM
select CRYPTO
select CRYPTO_HMAC
select CRYPTO_MD5
select CRYPTO_SHA1
43
---help---
Support for IPsec AH.
If unsure, say Y.
config INET_ESP
tristate "IP: ESP transformation"
select XFRM
select CRYPTO
select CRYPTO_HMAC
select CRYPTO_MD5
select CRYPTO_SHA1
select CRYPTO_DES
---help---
Support for IPsec ESP.
If unsure, say Y.
config INET_IPCOMP
tristate "IP: IPComp transformation"
select XFRM
select INET_TUNNEL
select CRYPTO
select CRYPTO_DEFLATE
---help---
Support for IP Paylod Compression (RFC3173), typically needed
for IPsec.
If unsure, say Y.
config INET_TUNNEL
tristate "IP: tunnel transformation"
select XFRM
---help---
Support for generic IP tunnel transformation, which is required by
the IP tunneling module as well as tunnel mode IPComp.
If unsure, say Y.
source "net/ipv4/ipvs/Kconfig"
44
2. linux/net/ipv4/Makefile
#
# Makefile for the Linux TCP/IP (INET) layer.
#
obj-y := utils.o route.o inetpeer.o protocol.o \
ip_input.o ip_fragment.o ip_forward.o ip_options.o \
ip_output.o ip_sockglue.o \
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o tcp_minisocks.o \
tcp_diag.o datagram.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \
sysctl_net_ipv4.o fib_frontend.o fib_semantics.o fib_hash.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
obj-$(CONFIG_IP_MROUTE) += ipmr.o
obj-$(CONFIG_NET_IPIP) += ipip.o
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_INET_AH) += ah4.o
obj-$(CONFIG_INET_ESP) += esp4.o
obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
obj-$(CONFIG_INET_TUNNEL) += xfrm4_tunnel.o
obj-$(CONFIG_IP_PNP) += ipconfig.o
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IP_VS) += ipvs/
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o
obj-$(CONFIG_IP_RTP_NAT) += rtpnat.o
45
3. linux/net/ipv4/af_inet.c
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* PF_INET protocol family socket handler.
*
* Version: $Id: af_inet.c,v 1.137 2002/02/01 22:01:03 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Florian La Roche, <flla@stud.uni-sb.de>
* Alan Cox, <A.Cox@swansea.ac.uk>
*
* Changes (see also sock.c)
*
* piggy,
* Karl Knutson : Socket protocol table
* A.N.Kuznetsov : Socket death error in accept().
* John Richardson : Fix non blocking error in connect()
* so sockets that fail to connect
* don't return -EINPROGRESS.
* Alan Cox : Asynchronous I/O support
* Alan Cox : Keep correct socket pointer on sock
* structures
* when accept() ed
* Alan Cox : Semantics of SO_LINGER aren't state
* moved to close when you look carefully.
* With this fixed and the accept bug fixed
* some RPC stuff seems happier.
* Niibe Yutaka : 4.4BSD style write async I/O
* Alan Cox,
* Tony Gale : Fixed reuse semantics.
* Alan Cox : bind() shouldn't abort existing but dead
* sockets. Stops FTP netin:.. I hope.
* Alan Cox : bind() works correctly for RAW sockets.
* Note that FreeBSD at least was broken
* in this respect so be careful with
* compatibility tests...
* Alan Cox : routing cache support
* Alan Cox : memzero the socket structure for
* compactness.
* Matt Day: nonblock connect error handler
* Alan Cox : Allow large numbers of pending sockets
* (eg for big web sites), but only if
46
* specifically application requested.
* Alan Cox : New buffering throughout IP. Used
* dumbly.
* Alan Cox : New buffering now used smartly.
* Alan Cox : BSD rather than common sense
* interpretation of listen.
* Germano Caronni : Assorted small races.
* Alan Cox : sendmsg/recvmsg basic support.
* Alan Cox : Only sendmsg/recvmsg now supported.
* Alan Cox : Locked down bind (see security list).
* Alan Cox : Loosened bind a little.
* Mike McLagan : ADD/DEL DLCI Ioctls
* Willy Konynenberg : Transparent proxying support.
* David S. Miller : New socket lookup architecture.
* Some other random speedups.
* Cyrus Durgin : Cleaned up file for kmod hacks.
* Andi Kleen : Fix inet_stream_connect TCP race.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/netfilter_ipv4.h>
#include <asm/uaccess.h>
47
#include <asm/system.h>
#include <linux/smp_lock.h>
#include <linux/inet.h>
#include <linux/igmp.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/arp.h>
#include <net/route.h>
#include <net/ip_fib.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/raw.h>
#include <net/icmp.h>
#include <net/ipip.h>
#include <net/inet_common.h>
#include <net/xfrm.h>
#ifdef CONFIG_IP_MROUTE
#include <linux/mroute.h>
#endif
DEFINE_SNMP_STAT(struct linux_mib, net_statistics);
#ifdef INET_REFCNT_DEBUG
atomic_t inet_sock_nr;
#endif
extern void ip_mc_drop_socket(struct sock *sk);
/* The inetsw table contains everything that inet_create needs to
* build a new socket.
*/
static struct list_head inetsw[SOCK_MAX];
static spinlock_t inetsw_lock = SPIN_LOCK_UNLOCKED;
/* New destruction routine */
void inet_sock_destruct(struct sock *sk)
{
struct inet_opt *inet = inet_sk(sk);
__skb_queue_purge(&sk->sk_receive_queue);
__skb_queue_purge(&sk->sk_error_queue);
48
if (sk->sk_type == SOCK_STREAM && sk->sk_state != TCP_CLOSE) {
printk("Attempt to release TCP socket in state %d %p\n",
sk->sk_state, sk);
return;
}
if (!sock_flag(sk, SOCK_DEAD)) {
printk("Attempt to release alive inet socket %p\n", sk);
return;
}
BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
BUG_TRAP(!sk->sk_wmem_queued);
BUG_TRAP(!sk->sk_forward_alloc);
if (inet->opt)
kfree(inet->opt);
dst_release(sk->sk_dst_cache);
#ifdef INET_REFCNT_DEBUG
atomic_dec(&inet_sock_nr);
printk(KERN_DEBUG "INET socket %p released, %d are still alive\n",
sk, atomic_read(&inet_sock_nr));
#endif
}
/*
* The routines beyond this point handle the behaviour of an AF_INET
* socket object. Mostly it punts to the subprotocols of IP to do
* the work.
*/
/*
* Automatically bind an unbound socket.
*/
static int inet_autobind(struct sock *sk)
{
struct inet_opt *inet;
/* We may need to bind the socket. */
lock_sock(sk);
inet = inet_sk(sk);
if (!inet->num) {
if (sk->sk_prot->get_port(sk, 0)) {
release_sock(sk);
return -EAGAIN;
49
}
inet->sport = htons(inet->num);
}
release_sock(sk);
return 0;
}
/*
* Move a socket into listening state.
*/
int inet_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
unsigned char old_state;
int err;
lock_sock(sk);
err = -EINVAL;
if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
goto out;
old_state = sk->sk_state;
if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))
goto out;
/* Really, if the socket is already in listen state
* we can only allow the backlog to be adjusted.
*/
if (old_state != TCP_LISTEN) {
err = tcp_listen_start(sk);
if (err)
goto out;
}
sk->sk_max_ack_backlog = backlog;
err = 0;
out:
release_sock(sk);
return err;
}
/*
* Create an inet socket.
*/
50
static int inet_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct list_head *p;
struct inet_protosw *answer;
struct inet_opt *inet;
struct proto *answer_prot;
unsigned char answer_flags;
char answer_no_check;
int err;
sock->state = SS_UNCONNECTED;
/* Look for the requested type/protocol pair. */
answer = NULL;
rcu_read_lock();
list_for_each_rcu(p, &inetsw[sock->type]) {
answer = list_entry(p, struct inet_protosw, list);
/* Check the non-wild match. */
if (protocol == answer->protocol) {
if (protocol != IPPROTO_IP)
break;
} else {
/* Check for the two wild cases. */
if (IPPROTO_IP == protocol) {
protocol = answer->protocol;
break;
}
if (IPPROTO_IP == answer->protocol)
break;
}
answer = NULL;
}
err = -ESOCKTNOSUPPORT;
if (!answer)
goto out_rcu_unlock;
err = -EPERM;
if (answer->capability > 0 && !capable(answer->capability))
goto out_rcu_unlock;
err = -EPROTONOSUPPORT;
if (!protocol)
goto out_rcu_unlock;
sock->ops = answer->ops;
51
answer_prot = answer->prot;
answer_no_check = answer->no_check;
answer_flags = answer->flags;
rcu_read_unlock();
BUG_TRAP(answer_prot->slab != NULL);
err = -ENOBUFS;
sk = sk_alloc(PF_INET, GFP_KERNEL,
answer_prot->slab_obj_size,
answer_prot->slab);
if (sk == NULL)
goto out;
err = 0;
sk->sk_prot = answer_prot;
sk->sk_no_check = answer_no_check;
if (INET_PROTOSW_REUSE & answer_flags)
sk->sk_reuse = 1;
inet = inet_sk(sk);
if (SOCK_RAW == sock->type) {
inet->num = protocol;
if (IPPROTO_RAW == protocol)
inet->hdrincl = 1;
}
if (ipv4_config.no_pmtu_disc)
inet->pmtudisc = IP_PMTUDISC_DONT;
else
inet->pmtudisc = IP_PMTUDISC_WANT;
inet->id = 0;
sock_init_data(sock, sk);
sk_set_owner(sk, THIS_MODULE);
sk->sk_destruct = inet_sock_destruct;
sk->sk_family = PF_INET;
sk->sk_protocol = protocol;
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
inet->uc_ttl = -1;
inet->mc_loop = 1;
inet->mc_ttl = 1;
52
inet->mc_index = 0;
inet->mc_list = NULL;
#ifdef INET_REFCNT_DEBUG
atomic_inc(&inet_sock_nr);
#endif
if (inet->num) {
/* It assumes that any protocol which allows
* the user to assign a number at socket
* creation time automatically
* shares.
*/
inet->sport = htons(inet->num);
/* Add to protocol hash chains. */
sk->sk_prot->hash(sk);
}
if (sk->sk_prot->init) {
err = sk->sk_prot->init(sk);
if (err)
sk_common_release(sk);
}
out:
return err;
out_rcu_unlock:
rcu_read_unlock();
goto out;
}
/*
* The peer socket should always be NULL (or else). When we call this
* function we are destroying the object and from then on nobody
* should refer to it.
*/
int inet_release(struct socket *sock)
{
struct sock *sk = sock->sk;
if (sk) {
long timeout;
/* Applications forget to leave groups before exiting */
ip_mc_drop_socket(sk);
53
/* If linger is set, we don't return until the close
* is complete. Otherwise we return immediately. The
* actually closing is done the same either way.
*
* If the close is due to the process exiting, we never
* linger..
*/
timeout = 0;
if (sock_flag(sk, SOCK_LINGER) &&
!(current->flags & PF_EXITING))
timeout = sk->sk_lingertime;
sock->sk = NULL;
sk->sk_prot->close(sk, timeout);
}
return 0;
}
/* It is off by default, see below. */
int sysctl_ip_nonlocal_bind;
int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
struct sock *sk = sock->sk;
struct inet_opt *inet = inet_sk(sk);
unsigned short snum;
int chk_addr_ret;
int err;
/* If the socket has its own bind function then use it. (RAW) */
if (sk->sk_prot->bind) {
err = sk->sk_prot->bind(sk, uaddr, addr_len);
goto out;
}
err = -EINVAL;
if (addr_len < sizeof(struct sockaddr_in))
goto out;
chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
/* Not specified by any standard per-se, however it breaks too
* many applications when removed. It is unfortunate since
* allowing applications to make a non-local bind solves
* several problems with systems using dynamic addressing.
* (ie. your servers still start up even if your ISDN link
* is temporarily down)
54
*/
err = -EADDRNOTAVAIL;
if (!sysctl_ip_nonlocal_bind &&
!inet->freebind &&
addr->sin_addr.s_addr != INADDR_ANY &&
chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST &&
chk_addr_ret != RTN_BROADCAST)
goto out;
snum = ntohs(addr->sin_port);
err = -EACCES;
if (snum && snum < PROT_SOCK &&
!capable(CAP_NET_BIND_SERVICE))
goto out;
/* We keep a pair of addresses. rcv_saddr is the one
* used by hash lookups, and saddr is used for transmit.
*
* In the BSD API these are the same except where it
* would be illegal to use them (multicast/broadcast) in
* which case the sending device address is used.
*/
lock_sock(sk);
/* Check these errors (active socket, double bind). */
err = -EINVAL;
if (sk->sk_state != TCP_CLOSE || inet->num)
goto out_release_sock;
inet->rcv_saddr = inet->saddr = addr->sin_addr.s_addr;
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret ==
RTN_BROADCAST)
inet->saddr = 0; /* Use device */
/* Make sure we are allowed to bind here. */
if (sk->sk_prot->get_port(sk, snum)) {
inet->saddr = inet->rcv_saddr = 0;
err = -EADDRINUSE;
goto out_release_sock;
}
if (inet->rcv_saddr)
sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
if (snum)
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
55
inet->sport = htons(inet->num);
inet->daddr = 0;
inet->dport = 0;
sk_dst_reset(sk);
err = 0;
out_release_sock:
release_sock(sk);
out:
return err;
}
int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
if (uaddr->sa_family == AF_UNSPEC)
return sk->sk_prot->disconnect(sk, flags);
if (!inet_sk(sk)->num && inet_autobind(sk))
return -EAGAIN;
return sk->sk_prot->connect(sk, (struct sockaddr *)uaddr, addr_len);
}
static long inet_wait_for_connect(struct sock *sk, long timeo)
{
DEFINE_WAIT(wait);
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
/* Basic assumption: if someone sets sk->sk_err, he _must_
* change state of the socket from TCP_SYN_*.
* Connect() does not allow to get error notifications
* without closing the socket.
*/
while ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
if (signal_pending(current) || !timeo)
break;
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
}
finish_wait(sk->sk_sleep, &wait);
return timeo;
}
56
/*
* Connect to a remote host. There is regrettably still a little
* TCP 'magic' in here.
*/
int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
int err;
long timeo;
lock_sock(sk);
if (uaddr->sa_family == AF_UNSPEC) {
err = sk->sk_prot->disconnect(sk, flags);
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
goto out;
}
switch (sock->state) {
default:
err = -EINVAL;
goto out;
case SS_CONNECTED:
err = -EISCONN;
goto out;
case SS_CONNECTING:
err = -EALREADY;
/* Fall out of switch with err, set for this state */
break;
case SS_UNCONNECTED:
err = -EISCONN;
if (sk->sk_state != TCP_CLOSE)
goto out;
err = sk->sk_prot->connect(sk, uaddr, addr_len);
if (err < 0)
goto out;
sock->state = SS_CONNECTING;
/* Just entered SS_CONNECTING state; the only
* difference is that return value in non-blocking
* case is EINPROGRESS, rather than EALREADY.
*/
57
err = -EINPROGRESS;
break;
}
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
/* Error code is set above */
if (!timeo || !inet_wait_for_connect(sk, timeo))
goto out;
err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
}
/* Connection was closed by RST, timeout, ICMP error
* or another process disconnected us.
*/
if (sk->sk_state == TCP_CLOSE)
goto sock_error;
/* sk->sk_err may be not zero now, if RECVERR was ordered by user
* and error was received after socket entered established state.
* Hence, it is handled normally after connect() return successfully.
*/
sock->state = SS_CONNECTED;
err = 0;
out:
release_sock(sk);
return err;
sock_error:
err = sock_error(sk) ? : -ECONNABORTED;
sock->state = SS_UNCONNECTED;
if (sk->sk_prot->disconnect(sk, flags))
sock->state = SS_DISCONNECTING;
goto out;
}
/*
* Accept a pending connection. The TCP layer now gives BSD semantics.
*/
int inet_accept(struct socket *sock, struct socket *newsock, int flags)
58
{
struct sock *sk1 = sock->sk;
int err = -EINVAL;
struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);
if (!sk2)
goto do_err;
lock_sock(sk2);
BUG_TRAP((1 << sk2->sk_state) &
(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
sock_graft(sk2, newsock);
newsock->state = SS_CONNECTED;
err = 0;
release_sock(sk2);
do_err:
return err;
}
/*
* This does both peername and sockname.
*/
int inet_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sock *sk = sock->sk;
struct inet_opt *inet = inet_sk(sk);
struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
sin->sin_family = AF_INET;
if (peer) {
if (!inet->dport ||
(((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) &&
peer == 1))
return -ENOTCONN;
sin->sin_port = inet->dport;
sin->sin_addr.s_addr = inet->daddr;
} else {
__u32 addr = inet->rcv_saddr;
if (!addr)
addr = inet->saddr;
sin->sin_port = inet->sport;
59
sin->sin_addr.s_addr = addr;
}
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
*uaddr_len = sizeof(*sin);
return 0;
}
int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size)
{
struct sock *sk = sock->sk;
/* We may need to bind the socket. */
if (!inet_sk(sk)->num && inet_autobind(sk))
return -EAGAIN;
return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int
flags)
{
struct sock *sk = sock->sk;
/* We may need to bind the socket. */
if (!inet_sk(sk)->num && inet_autobind(sk))
return -EAGAIN;
if (sk->sk_prot->sendpage)
return sk->sk_prot->sendpage(sk, page, offset, size, flags);
return sock_no_sendpage(sock, page, offset, size, flags);
}
int inet_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
int err = 0;
/* This should really check to make sure
* the socket is a TCP socket. (WHY AC...)
*/
how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
1->2 bit 2 snds.
2->3 */
60
if ((how & ~SHUTDOWN_MASK) || !how) /* MAXINT->0 */
return -EINVAL;
lock_sock(sk);
if (sock->state == SS_CONNECTING) {
if ((1 << sk->sk_state) &
(TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_CLOSE))
sock->state = SS_DISCONNECTING;
else
sock->state = SS_CONNECTED;
}
switch (sk->sk_state) {
case TCP_CLOSE:
err = -ENOTCONN;
/* Hack to wake up other listeners, who can poll for
POLLHUP, even on eg. unconnected UDP sockets -- RR */
default:
sk->sk_shutdown |= how;
if (sk->sk_prot->shutdown)
sk->sk_prot->shutdown(sk, how);
break;
/* Remaining two branches are temporary solution for missing
* close() in multithreaded environment. It is _not_ a good idea,
* but we have no choice until close() is repaired at VFS level.
*/
case TCP_LISTEN:
if (!(how & RCV_SHUTDOWN))
break;
/* Fall through */
case TCP_SYN_SENT:
err = sk->sk_prot->disconnect(sk, O_NONBLOCK);
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
break;
}
/* Wake up anyone sleeping in poll. */
sk->sk_state_change(sk);
release_sock(sk);
return err;
}
/*
* ioctl() calls you can issue on an INET socket. Most of these are
* device configuration and stuff and very rarely used. Some ioctls
61
* pass on to the socket itself.
*
* NOTE: I like the idea of a module for the config stuff. ie ifconfig
* loads the devconfigure module does its configuring and unloads it.
* There's a good 20K of config code hanging around the kernel.
*/
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err = 0;
switch (cmd) {
case SIOCGSTAMP:
err = sock_get_timestamp(sk, (struct timeval __user *)arg);
break;
case SIOCADDRT:
case SIOCDELRT:
case SIOCRTMSG:
err = ip_rt_ioctl(cmd, (void __user *)arg);
break;
case SIOCDARP:
case SIOCGARP:
case SIOCSARP:
err = arp_ioctl(cmd, (void __user *)arg);
break;
case SIOCGIFADDR:
case SIOCSIFADDR:
case SIOCGIFBRDADDR:
case SIOCSIFBRDADDR:
case SIOCGIFNETMASK:
case SIOCSIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCSIFDSTADDR:
case SIOCSIFPFLAGS:
case SIOCGIFPFLAGS:
case SIOCSIFFLAGS:
err = devinet_ioctl(cmd, (void __user *)arg);
break;
default:
if (!sk->sk_prot->ioctl ||
(err = sk->sk_prot->ioctl(sk, cmd, arg)) ==
-
ENOIOCTLCMD)
err = dev_ioctl(cmd, (void __user *)arg);
break;
62
}
return err;
}
struct proto_ops inet_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
.connect = inet_stream_connect,
.socketpair = sock_no_socketpair,
.accept = inet_accept,
.getname = inet_getname,
.poll = tcp_poll,
.ioctl = inet_ioctl,
.listen = inet_listen,
.shutdown = inet_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = tcp_sendpage
};
struct proto_ops inet_dgram_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
.connect = inet_dgram_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = inet_getname,
.poll = datagram_poll,
.ioctl = inet_ioctl,
.listen = sock_no_listen,
.shutdown = inet_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
};
63
static struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
extern void tcp_init(void);
extern void tcp_v4_init(struct net_proto_family *);
/* Upon startup we insert all the elements in inetsw_array[] into
* the linked list inetsw.
*/
static struct inet_protosw inetsw_array[] =
{
{
.type = SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot = &tcp_prot,
.ops = &inet_stream_ops,
.capability = -1,
.no_check = 0,
.flags = INET_PROTOSW_PERMANENT,
},
{
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,
.ops = &inet_dgram_ops,
.capability = -1,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_PERMANENT,
},
{
.type = SOCK_RAW,
.protocol = IPPROTO_IP, /* wild card */
.prot = &raw_prot,
.ops = &inet_dgram_ops,
.capability = CAP_NET_RAW,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_REUSE,
}
};
64
#define INETSW_ARRAY_LEN (sizeof(inetsw_array) / sizeof(struct inet_protosw))
void inet_register_protosw(struct inet_protosw *p)
{
struct list_head *lh;
struct inet_protosw *answer;
int protocol = p->protocol;
struct list_head *last_perm;
spin_lock_bh(&inetsw_lock);
if (p->type >= SOCK_MAX)
goto out_illegal;
/* If we are trying to override a permanent protocol, bail. */
answer = NULL;
last_perm = &inetsw[p->type];
list_for_each(lh, &inetsw[p->type]) {
answer = list_entry(lh, struct inet_protosw, list);
/* Check only the non-wild match. */
if (INET_PROTOSW_PERMANENT & answer->flags) {
if (protocol == answer->protocol)
break;
last_perm = lh;
}
answer = NULL;
}
if (answer)
goto out_permanent;
/* Add the new entry after the last permanent entry if any, so that
* the new entry does not override a permanent entry when matched with
* a wild-card protocol. But it is allowed to override any existing
* non-permanent entry. This means that when we remove this entry, the
* system automatically returns to the old behavior.
*/
list_add_rcu(&p->list, last_perm);
out:
spin_unlock_bh(&inetsw_lock);
synchronize_net();
return;
65
out_permanent:
printk(KERN_ERR "Attempt to override permanent protocol %d.\n",
protocol);
goto out;
out_illegal:
printk(KERN_ERR
"Ignoring attempt to register invalid socket type %d.\n",
p->type);
goto out;
}
void inet_unregister_protosw(struct inet_protosw *p)
{
if (INET_PROTOSW_PERMANENT & p->flags) {
printk(KERN_ERR
"Attempt to unregister permanent protocol %d.\n",
p->protocol);
} else {
spin_lock_bh(&inetsw_lock);
list_del_rcu(&p->list);
spin_unlock_bh(&inetsw_lock);
synchronize_net();
}
}
#ifdef CONFIG_IP_MULTICAST
static struct net_protocol igmp_protocol = {
.handler = igmp_rcv,
};
#endif
static struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.no_policy = 1,
};
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
66
static struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
};
static int __init init_ipv4_mibs(void)
{
net_statistics[0] = alloc_percpu(struct linux_mib);
net_statistics[1] = alloc_percpu(struct linux_mib);
ip_statistics[0] = alloc_percpu(struct ipstats_mib);
ip_statistics[1] = alloc_percpu(struct ipstats_mib);
icmp_statistics[0] = alloc_percpu(struct icmp_mib);
icmp_statistics[1] = alloc_percpu(struct icmp_mib);
tcp_statistics[0] = alloc_percpu(struct tcp_mib);
tcp_statistics[1] = alloc_percpu(struct tcp_mib);
udp_statistics[0] = alloc_percpu(struct udp_mib);
udp_statistics[1] = alloc_percpu(struct udp_mib);
if (!
(net_statistics[0] && net_statistics[1] && ip_statistics[0]
&& ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1]
&& udp_statistics[0] && udp_statistics[1]))
return -ENOMEM;
(void) tcp_mib_init();
return 0;
}
int ipv4_proc_init(void);
extern void ipfrag_init(void);
#ifdef CONFIG_IP_RTP_NAT
extern void rtpnat_init(void);
#endif
static int __init inet_init(void)
{
struct sk_buff *dummy_skb;
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL;
if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {
printk(KERN_CRIT "%s: panic\n", __FUNCTION__);
goto out;
}
67
rc = sk_alloc_slab(&tcp_prot, "tcp_sock");
if (rc) {
sk_alloc_slab_error(&tcp_prot);
goto out;
}
rc = sk_alloc_slab(&udp_prot, "udp_sock");
if (rc) {
sk_alloc_slab_error(&udp_prot);
goto out_tcp_free_slab;
}
rc = sk_alloc_slab(&raw_prot, "raw_sock");
if (rc) {
sk_alloc_slab_error(&raw_prot);
goto out_udp_free_slab;
}
/*
* Tell SOCKET that we are alive...
*/
(void)sock_register(&inet_family_ops);
/*
* Add all the base protocols.
*/
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
#endif
/* Register the socket-side information for inet_create. */
for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q);
/*
* Set the ARP module up
68
*/
arp_init();
/*
* Set the IP module up
*/
ip_init();
tcp_v4_init(&inet_family_ops);
/* Setup TCP slab cache for open requests. */
tcp_init();
/*
* Set the ICMP layer up
*/
icmp_init(&inet_family_ops);
/*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
ip_mr_init();
#endif
/*
* Initialise per-cpu ipv4 mibs
*/
if(init_ipv4_mibs())
printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ;
ipv4_proc_init();
ipfrag_init();
#ifdef CONFIG_IP_RTP_NAT
rtpnat_init();
#endif
rc = 0;
out:
return rc;
69
out_tcp_free_slab:
sk_free_slab(&tcp_prot);
out_udp_free_slab:
sk_free_slab(&udp_prot);
goto out;
}
module_init(inet_init);
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_PROC_FS
extern int fib_proc_init(void);
extern void fib_proc_exit(void);
extern int ip_misc_proc_init(void);
extern int raw_proc_init(void);
extern void raw_proc_exit(void);
extern int tcp4_proc_init(void);
extern void tcp4_proc_exit(void);
extern int udp4_proc_init(void);
extern void udp4_proc_exit(void);
int __init ipv4_proc_init(void)
{
int rc = 0;
if (raw_proc_init())
goto out_raw;
if (tcp4_proc_init())
goto out_tcp;
if (udp4_proc_init())
goto out_udp;
if (fib_proc_init())
goto out_fib;
if (ip_misc_proc_init())
goto out_misc;
out:
return rc;
out_misc:
fib_proc_exit();
out_fib:
udp4_proc_exit();
out_udp:
tcp4_proc_exit();
out_tcp:
raw_proc_exit();
70
out_raw:
rc = -ENOMEM;
goto out;
}
#else /* CONFIG_PROC_FS */
int __init ipv4_proc_init(void)
{
return 0;
}
#endif /* CONFIG_PROC_FS */
MODULE_ALIAS_NETPROTO(PF_INET);
EXPORT_SYMBOL(inet_accept);
EXPORT_SYMBOL(inet_bind);
EXPORT_SYMBOL(inet_dgram_connect);
EXPORT_SYMBOL(inet_dgram_ops);
EXPORT_SYMBOL(inet_getname);
EXPORT_SYMBOL(inet_ioctl);
EXPORT_SYMBOL(inet_listen);
EXPORT_SYMBOL(inet_register_protosw);
EXPORT_SYMBOL(inet_release);
EXPORT_SYMBOL(inet_sendmsg);
EXPORT_SYMBOL(inet_shutdown);
EXPORT_SYMBOL(inet_sock_destruct);
EXPORT_SYMBOL(inet_stream_connect);
EXPORT_SYMBOL(inet_stream_ops);
EXPORT_SYMBOL(inet_unregister_protosw);
EXPORT_SYMBOL(net_statistics);
EXPORT_SYMBOL(tcp_protocol);
EXPORT_SYMBOL(udp_protocol);
#ifdef INET_REFCNT_DEBUG
EXPORT_SYMBOL(inet_sock_nr);
#endif
71
4. linux/net/ipv4/ip_input.c
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* The Internet Protocol (IP) module.
*
* Version: $Id: ip_input.c,v 1.55 2002/01/12 07:39:45 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Donald Becker, <becker@super.org>
* Alan Cox, <Alan.Cox@linux.org>
* Richard Underwood
* Stefan Becker, <stefanb@yello.ping.de>
* Jorge Cwik, <jorge@laser.satlink.net>
* Arnt Gulbrandsen, <agulbra@nvg.unit.no>
*
*
* Fixes:
* Alan Cox : Commented a couple of minor bits of
surplus code
* Alan Cox : Undefining IP_FORWARD doesn't
include the code
* (just stops a compiler warning).
* Alan Cox : Frames with >=MAX_ROUTE record
routes, strict routes or loose routes
* are junked rather than corrupting things.
* Alan Cox : Frames to bad broadcast subnets are
dumped
* We used to process them non broadcast
and
* boy could that cause havoc.
* Alan Cox : ip_forward sets the free flag on the
* new frame it queues. Still crap because
* it copies the frame but at least it
* doesn't eat memory too.
* Alan Cox : Generic queue code and memory fixes.
* Fred Van Kempen : IP fragment support (borrowed from
NET2E)
* Gerhard Koerting: Forward fragmented frames correctly.
* Gerhard Koerting: Fixes to my fix of the above 8-).
* Gerhard Koerting: IP interface addressing fix.
* Linus Torvalds : More robustness checks
72
* Alan Cox : Even more checks: Still not as robust as it
ought to be
* Alan Cox : Save IP header pointer for later
* Alan Cox : ip option setting
* Alan Cox : Use ip_tos/ip_ttl settings
* Alan Cox : Fragmentation bogosity removed
* (Thanks to Mark.Bush@prg.ox.ac.uk)
* Dmitry Gorodchanin : Send of a raw packet crash fix.
* Alan Cox : Silly ip bug when an overlength
* fragment turns up. Now frees the
* queue.
* Linus Torvalds/ : Memory leakage on fragmentation
* Alan Cox : handling.
* Gerhard Koerting: Forwarding uses IP priority hints
* Teemu Rantanen : Fragment problems.
* Alan Cox : General cleanup, comments and reformat
* Alan Cox : SNMP statistics
* Alan Cox : BSD address rule semantics. Also see
* UDP as there is a nasty checksum issue
* if you do things the wrong way.
* Alan Cox : Always defrag, moved IP_FORWARD to
the config.in file
* Alan Cox : IP options adjust sk->priority.
* Pedro Roque : Fix mtu/length error in ip_forward.
* Alan Cox : Avoid ip_chk_addr when possible.
* Richard Underwood : IP multicasting.
* Alan Cox : Cleaned up multicast handlers.
* Alan Cox : RAW sockets demultiplex in the BSD
style.
* Gunther Mayer : Fix the SNMP reporting typo
* Alan Cox : Always in group 224.0.0.1
* Pauline Middelink : Fast ip_checksum update when forwarding
* Masquerading support.
* Alan Cox : Multicast loopback error for 224.0.0.1
* Alan Cox : IP_MULTICAST_LOOP option.
* Alan Cox : Use notifiers.
* Bjorn Ekwall : Removed ip_csum (from slhc.c too)
* Bjorn Ekwall : Moved ip_fast_csum to ip.h (inline!)
* Stefan Becker : Send out ICMP HOST REDIRECT
* Arnt Gulbrandsen : ip_build_xmit
* Alan Cox : Per socket routing cache
* Alan Cox : Fixed routing cache, added header cache.
* Alan Cox : Loopback didn't work right in original
ip_build_xmit - fixed it.
* Alan Cox : Only send ICMP_REDIRECT if src/dest
are the same net.
73
* Alan Cox : Incoming IP option handling.
* Alan Cox : Set saddr on raw output frames as per
BSD.
* Alan Cox : Stopped broadcast source route
explosions.
* Alan Cox : Can disable source routing
* Takeshi Sone : Masquerading didn't work.
* Dave Bonn,Alan Cox : Faster IP forwarding whenever possible.
* Alan Cox : Memory leaks, tramples, misc debugging.
* Alan Cox : Fixed multicast (by popular demand 8))
* Alan Cox : Fixed forwarding (by even more popular
demand 8))
* Alan Cox : Fixed SNMP statistics [I think]
* Gerhard Koerting : IP fragmentation forwarding fix
* Alan Cox : Device lock against page fault.
* Alan Cox : IP_HDRINCL facility.
* Werner Almesberger : Zero fragment bug
* Alan Cox : RAW IP frame length bug
* Alan Cox : Outgoing firewall on build_xmit
* A.N.Kuznetsov : IP_OPTIONS support throughout the
kernel
* Alan Cox : Multicast routing hooks
* Jos Vos : Do accounting *before* call_in_firewall
* Willy Konynenberg : Transparent proxying support
*
*
*
* To Fix:
* IP fragmentation wants rewriting cleanly. The RFC815 algorithm is
much more efficient
* and could be made very efficient with the addition of some virtual
memory hacks to permit
* the allocation of a buffer that can then be 'grown' by twiddling page
tables.
* Output fragmentation wants updating along with the buffer
management to use a single
* interleaved copy algorithm so that fragmenting has a one copy
overhead. Actual packet
* output should probably do its own fragmentation at the UDP/RAW
layer. TCP shouldn't cause
* fragmentation anyway.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
74
*/
#include <asm/system.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/config.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <net/snmp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
#include <net/icmp.h>
#include <net/raw.h>
#include <net/checksum.h>
#include <linux/netfilter_ipv4.h>
#include <net/xfrm.h>
#include <linux/mroute.h>
#include <linux/netlink.h>
#ifdef CONFIG_IP_RTP_NAT
extern int rtpnat_process_skb (struct sk_buff *skb, struct net_device *dev);
#endif
/*
* SNMP management statistics
*/
DEFINE_SNMP_STAT(struct ipstats_mib, ip_statistics);
/*
* Process Router Attention IP option
*/
75
int ip_call_ra_chain(struct sk_buff *skb)
{
struct ip_ra_chain *ra;
u8 protocol = skb->nh.iph->protocol;
struct sock *last = NULL;
read_lock(&ip_ra_lock);
for (ra = ip_ra_chain; ra; ra = ra->next) {
struct sock *sk = ra->sk;
/* If socket is bound to an interface, only report
* the packet if it came from that interface.
*/
if (sk && inet_sk(sk)->num == protocol &&
(!sk->sk_bound_dev_if ||
sk->sk_bound_dev_if == skb->dev->ifindex)) {
if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
skb = ip_defrag(skb);
if (skb == NULL) {
read_unlock(&ip_ra_lock);
return 1;
}
}
if (last) {
struct sk_buff *skb2 = skb_clone(skb,
GFP_ATOMIC);
if (skb2)
raw_rcv(last, skb2);
}
last = sk;
}
}
if (last) {
raw_rcv(last, skb);
read_unlock(&ip_ra_lock);
return 1;
}
read_unlock(&ip_ra_lock);
return 0;
}
static inline int ip_local_deliver_finish(struct sk_buff *skb)
{
int ihl = skb->nh.iph->ihl*4;
76
#ifdef CONFIG_NETFILTER_DEBUG
nf_debug_ip_local_deliver(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/
__skb_pull(skb, ihl);
/* Free reference early: we don't need it any more, and it may
hold ip_conntrack module loaded indefinitely. */
nf_reset(skb);
/* Point into the IP datagram, just past the header. */
skb->h.raw = skb->data;
rcu_read_lock();
{
/* Note: See raw.c and net/raw.h,
RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
int protocol = skb->nh.iph->protocol;
int hash;
struct sock *raw_sk;
struct net_protocol *ipprot;
resubmit:
hash = protocol & (MAX_INET_PROTOS - 1);
raw_sk = sk_head(&raw_v4_htable[hash]);
/* If there maybe a raw socket we must check - if not we
* don't care less
*/
if (raw_sk)
raw_v4_input(skb, skb->nh.iph, hash);
if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) {
int ret;
if (!ipprot->no_policy &&
!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
{
kfree_skb(skb);
goto out;
}
ret = ipprot->handler(skb);
if (ret < 0) {
protocol = -ret;
goto resubmit;
}
77
IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
} else {
if (!raw_sk) {
if (xfrm4_policy_check(NULL,
XFRM_POLICY_IN, skb)) {
IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
icmp_send(skb,
ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0);
}
} else
IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
kfree_skb(skb);
}
}
out:
rcu_read_unlock();
return 0;
}
/*
* Deliver IP Packets to the higher protocol layers.
*/
int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
*/
if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
skb = ip_defrag(skb);
if (!skb)
return 0;
}
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
static inline int ip_rcv_finish(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct iphdr *iph = skb->nh.iph;
78
/*
* Initialise the virtual path cache for the packet. It describes
* how the packet travels inside Linux networking.
*/
if (skb->dst == NULL) {
if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
goto drop;
}
#ifdef CONFIG_NET_CLS_ROUTE
if (skb->dst->tclassid) {
struct ip_rt_acct *st = ip_rt_acct + 256*smp_processor_id();
u32 idx = skb->dst->tclassid;
st[idx&0xFF].o_packets++;
st[idx&0xFF].o_bytes+=skb->len;
st[(idx>>16)&0xFF].i_packets++;
st[(idx>>16)&0xFF].i_bytes+=skb->len;
}
#endif
if (iph->ihl > 5) {
struct ip_options *opt;
/* It looks as overkill, because not all
IP options require packet mangling.
But it is the easiest for now, especially taking
into account that combination of IP options
and running sniffer is extremely rare condition.
--ANK (980813)
*/
if (skb_cow(skb, skb_headroom(skb))) {
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto drop;
}
iph = skb->nh.iph;
if (ip_options_compile(NULL, skb))
goto inhdr_error;
opt = &(IPCB(skb)->opt);
if (opt->srr) {
struct in_device *in_dev = in_dev_get(dev);
if (in_dev) {
if (!IN_DEV_SOURCE_ROUTE(in_dev)) {
79
if (IN_DEV_LOG_MARTIANS(in_dev)
&& net_ratelimit())
printk(KERN_INFO "source
route option %u.%u.%u.%u -> %u.%u.%u.%u\n",
NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
in_dev_put(in_dev);
goto drop;
}
in_dev_put(in_dev);
}
if (ip_options_rcv_srr(skb))
goto drop;
}
}
return dst_input(skb);
inhdr_error:
IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
/*
* Main IP Receive routine.
*/
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
struct iphdr *iph;
/* When the interface is in promisc. mode, drop all the crap
* that it receives, do not try to analyse it.
*/
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto out;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
80
goto inhdr_error;
iph = skb->nh.iph;
/*
* RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the
checksum.
*
* Is the datagram acceptable?
*
* 1. Length at least the size of an ip header
* 2. Version of 4
* 3. Checksums correctly. [Speed optimisation for later, skip
loopback checksums]
* 4. Doesn't have a bogus length
*/
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = skb->nh.iph;
if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
goto inhdr_error;
{
__u32 len = ntohs(iph->tot_len);
if (skb->len < len || len < (iph->ihl<<2))
goto inhdr_error;
/* Our transport medium may have padded the buffer out. Now we
know it
* is IP we can trim to the true length of the frame.
* Note this now means skb->len holds ntohs(iph->tot_len).
*/
if (skb->len > len) {
__pskb_trim(skb, len);
if (skb->ip_summed == CHECKSUM_HW)
skb->ip_summed = CHECKSUM_NONE;
}
}
#ifdef CONFIG_IP_RTP_NAT
81
{
int err;
err = rtpnat_process_skb (skb, dev);
if (0 == err)
return 0;
else if (2 == err)
goto drop;
}
#endif
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
inhdr_error:
IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out:
return NET_RX_DROP;
}
EXPORT_SYMBOL(ip_rcv);
EXPORT_SYMBOL(ip_statistics);
82
5. linux/net/ipv4/ip_sockglue.c
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* The IP to API glue.
*
* Version: $Id: ip_sockglue.c,v 1.62 2002/02/01 22:01:04 davem Exp $
*
* Authors: see ip.c
*
* Fixes:
* Many : Split from ip.c , see ip.c for history.
* Martin Mares : TOS setting fixed.
* Alan Cox : FixEd a couple of oopses in Martin's
* TOS tweaks.
* Mike McLagan : Routing by source
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/tcp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/igmp.h>
#include <linux/netfilter.h>
#include <linux/route.h>
#include <linux/mroute.h>
#include <net/route.h>
#include <net/xfrm.h>
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#include <net/transp_v6.h>
#endif
#include <linux/errqueue.h>
83
#include <asm/uaccess.h>
#ifdef CONFIG_IP_RTP_NAT
#include <linux/rtpnat.h>
#endif
#define IP_CMSG_PKTINFO 1
#define IP_CMSG_TTL 2
#define IP_CMSG_TOS 4
#define IP_CMSG_RECVOPTS 8
#define IP_CMSG_RETOPTS 16
/*
* SOL_IP control messages.
*/
static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
{
struct in_pktinfo info;
struct rtable *rt = (struct rtable *)skb->dst;
info.ipi_addr.s_addr = skb->nh.iph->daddr;
if (rt) {
info.ipi_ifindex = rt->rt_iif;
info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
} else {
info.ipi_ifindex = 0;
info.ipi_spec_dst.s_addr = 0;
}
put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
}
static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
{
int ttl = skb->nh.iph->ttl;
put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
}
static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
{
put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos);
}
static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
{
84
if (IPCB(skb)->opt.optlen == 0)
return;
put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb-
>nh.iph+1);
}
void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
{
unsigned char optbuf[sizeof(struct ip_options) + 40];
struct ip_options * opt = (struct ip_options*)optbuf;
if (IPCB(skb)->opt.optlen == 0)
return;
if (ip_options_echo(opt, skb)) {
msg->msg_flags |= MSG_CTRUNC;
return;
}
ip_options_undo(opt);
put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
}
void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
{
struct inet_opt *inet = inet_sk(skb->sk);
unsigned flags = inet->cmsg_flags;
/* Ordered by supposed usage frequency */
if (flags & 1)
ip_cmsg_recv_pktinfo(msg, skb);
if ((flags>>=1) == 0)
return;
if (flags & 1)
ip_cmsg_recv_ttl(msg, skb);
if ((flags>>=1) == 0)
return;
if (flags & 1)
ip_cmsg_recv_tos(msg, skb);
if ((flags>>=1) == 0)
return;
85
if (flags & 1)
ip_cmsg_recv_opts(msg, skb);
if ((flags>>=1) == 0)
return;
if (flags & 1)
ip_cmsg_recv_retopts(msg, skb);
}
int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
{
int err;
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg,
cmsg)) {
if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
(unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+ cmsg->cmsg_len) > msg->msg_controllen) {
return -EINVAL;
}
if (cmsg->cmsg_level != SOL_IP)
continue;
switch (cmsg->cmsg_type) {
case IP_RETOPTS:
err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct
cmsghdr));
err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err <
40 ? err : 40, 0);
if (err)
return err;
break;
case IP_PKTINFO:
{
struct in_pktinfo *info;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct
in_pktinfo)))
return -EINVAL;
info = (struct in_pktinfo *)CMSG_DATA(cmsg);
ipc->oif = info->ipi_ifindex;
ipc->addr = info->ipi_spec_dst.s_addr;
break;
}
default:
return -EINVAL;
86
}
}
return 0;
}
/* Special input handler for packets caught by router alert option.
They are selected only by protocol field, and then processed likely
local ones; but only if someone wants them! Otherwise, router
not running rsvpd will kill RSVP.
It is user level problem, what it will make with them.
I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
but receiver should be enough clever f.e. to forward mtrace requests,
sent to multicast group to reach destination designated router.
*/
struct ip_ra_chain *ip_ra_chain;
rwlock_t ip_ra_lock = RW_LOCK_UNLOCKED;
int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *))
{
struct ip_ra_chain *ra, *new_ra, **rap;
if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num == IPPROTO_RAW)
return -EINVAL;
new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
write_lock_bh(&ip_ra_lock);
for (rap = &ip_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
if (ra->sk == sk) {
if (on) {
write_unlock_bh(&ip_ra_lock);
if (new_ra)
kfree(new_ra);
return -EADDRINUSE;
}
*rap = ra->next;
write_unlock_bh(&ip_ra_lock);
if (ra->destructor)
ra->destructor(sk);
sock_put(sk);
kfree(ra);
return 0;
}
87
}
if (new_ra == NULL) {
write_unlock_bh(&ip_ra_lock);
return -ENOBUFS;
}
new_ra->sk = sk;
new_ra->destructor = destructor;
new_ra->next = ra;
*rap = new_ra;
sock_hold(sk);
write_unlock_bh(&ip_ra_lock);
return 0;
}
void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
u16 port, u32 info, u8 *payload)
{
struct inet_opt *inet = inet_sk(sk);
struct sock_exterr_skb *serr;
if (!inet->recverr)
return;
skb = skb_clone(skb, GFP_ATOMIC);
if (!skb)
return;
serr = SKB_EXT_ERR(skb);
serr->ee.ee_errno = err;
serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
serr->ee.ee_type = skb->h.icmph->type;
serr->ee.ee_code = skb->h.icmph->code;
serr->ee.ee_pad = 0;
serr->ee.ee_info = info;
serr->ee.ee_data = 0;
serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb-
>nh.raw;
serr->port = port;
skb->h.raw = payload;
if (!skb_pull(skb, payload - skb->data) ||
sock_queue_err_skb(sk, skb))
kfree_skb(skb);
}
88
void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
{
struct inet_opt *inet = inet_sk(sk);
struct sock_exterr_skb *serr;
struct iphdr *iph;
struct sk_buff *skb;
if (!inet->recverr)
return;
skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
if (!skb)
return;
iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr));
skb->nh.iph = iph;
iph->daddr = daddr;
serr = SKB_EXT_ERR(skb);
serr->ee.ee_errno = err;
serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
serr->ee.ee_type = 0;
serr->ee.ee_code = 0;
serr->ee.ee_pad = 0;
serr->ee.ee_info = info;
serr->ee.ee_data = 0;
serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
serr->port = port;
skb->h.raw = skb->tail;
__skb_pull(skb, skb->tail - skb->data);
if (sock_queue_err_skb(sk, skb))
kfree_skb(skb);
}
/*
* Handle MSG_ERRQUEUE
*/
int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
{
struct sock_exterr_skb *serr;
struct sk_buff *skb, *skb2;
struct sockaddr_in *sin;
struct {
89
struct sock_extended_err ee;
struct sockaddr_in offender;
} errhdr;
int err;
int copied;
err = -EAGAIN;
skb = skb_dequeue(&sk->sk_error_queue);
if (skb == NULL)
goto out;
copied = skb->len;
if (copied > len) {
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (err)
goto out_free_skb;
sock_recv_timestamp(msg, sk, skb);
serr = SKB_EXT_ERR(skb);
sin = (struct sockaddr_in *)msg->msg_name;
if (sin) {
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset);
sin->sin_port = serr->port;
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
}
memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
sin = &errhdr.offender;
sin->sin_family = AF_UNSPEC;
if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
struct inet_opt *inet = inet_sk(sk);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = skb->nh.iph->saddr;
sin->sin_port = 0;
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
if (inet->cmsg_flags)
ip_cmsg_recv(msg, skb);
}
90
put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
/* Now we could try to dump offended packet options */
msg->msg_flags |= MSG_ERRQUEUE;
err = copied;
/* Reset and regenerate socket error */
spin_lock_irq(&sk->sk_error_queue.lock);
sk->sk_err = 0;
if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
spin_unlock_irq(&sk->sk_error_queue.lock);
sk->sk_error_report(sk);
} else
spin_unlock_irq(&sk->sk_error_queue.lock);
out_free_skb:
kfree_skb(skb);
out:
return err;
}
/*
* Socket option code for IP. This is the end of the line after any TCP,UDP etc
options on
* an IP socket.
*/
int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen)
{
struct inet_opt *inet = inet_sk(sk);
int val=0,err;
if (level != SOL_IP)
return -ENOPROTOOPT;
if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) |
(1<<IP_RECVOPTS) | (1<<IP_RECVTOS) |
(1<<IP_RETOPTS) | (1<<IP_TOS) |
(1<<IP_TTL) | (1<<IP_HDRINCL) |
(1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) |
(1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) ||
optname == IP_MULTICAST_TTL ||
optname == IP_MULTICAST_LOOP) {
91
if (optlen >= sizeof(int)) {
if (get_user(val, (int __user *) optval))
return -EFAULT;
} else if (optlen >= sizeof(char)) {
unsigned char ucval;
if (get_user(ucval, (unsigned char __user *) optval))
return -EFAULT;
val = (int) ucval;
}
}
/* If optlen==0, it is equivalent to val == 0 */
#ifdef CONFIG_IP_MROUTE
if (optname >= MRT_BASE && optname <= (MRT_BASE + 10))
return ip_mroute_setsockopt(sk,optname,optval,optlen);
#endif
err = 0;
lock_sock(sk);
switch (optname) {
case IP_OPTIONS:
{
struct ip_options * opt = NULL;
if (optlen > 40 || optlen < 0)
goto e_inval;
err = ip_options_get(&opt, optval, optlen, 1);
if (err)
break;
if (sk->sk_type == SOCK_STREAM) {
struct tcp_opt *tp = tcp_sk(sk);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
if (sk->sk_family == PF_INET ||
(!((1 << sk->sk_state) &
(TCPF_LISTEN | TCPF_CLOSE)) &&
inet->daddr != LOOPBACK4_IPV6)) {
#endif
if (inet->opt)
tp->ext_header_len -= inet-
>opt->optlen;
if (opt)
tp->ext_header_len += opt-
>optlen;
tcp_sync_mss(sk, tp->pmtu_cookie);
92
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
}
#endif
}
opt = xchg(&inet->opt, opt);
if (opt)
kfree(opt);
break;
}
case IP_PKTINFO:
if (val)
inet->cmsg_flags |= IP_CMSG_PKTINFO;
else
inet->cmsg_flags &= ~IP_CMSG_PKTINFO;
break;
case IP_RECVTTL:
if (val)
inet->cmsg_flags |= IP_CMSG_TTL;
else
inet->cmsg_flags &= ~IP_CMSG_TTL;
break;
case IP_RECVTOS:
if (val)
inet->cmsg_flags |= IP_CMSG_TOS;
else
inet->cmsg_flags &= ~IP_CMSG_TOS;
break;
case IP_RECVOPTS:
if (val)
inet->cmsg_flags |= IP_CMSG_RECVOPTS;
else
inet->cmsg_flags &= ~IP_CMSG_RECVOPTS;
break;
case IP_RETOPTS:
if (val)
inet->cmsg_flags |= IP_CMSG_RETOPTS;
else
inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
break;
case IP_TOS: /* This sets both TOS and Precedence */
if (sk->sk_type == SOCK_STREAM) {
val &= ~3;
val |= inet->tos & 3;
}
if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP &&
!capable(CAP_NET_ADMIN)) {
93
err = -EPERM;
break;
}
if (inet->tos != val) {
inet->tos = val;
sk->sk_priority = rt_tos2priority(val);
sk_dst_reset(sk);
}
break;
case IP_TTL:
if (optlen<1)
goto e_inval;
if (val != -1 && (val < 1 || val>255))
goto e_inval;
inet->uc_ttl = val;
break;
case IP_HDRINCL:
if (sk->sk_type != SOCK_RAW) {
err = -ENOPROTOOPT;
break;
}
inet->hdrincl = val ? 1 : 0;
break;
case IP_MTU_DISCOVER:
if (val<0 || val>2)
goto e_inval;
inet->pmtudisc = val;
break;
case IP_RECVERR:
inet->recverr = !!val;
if (!val)
skb_queue_purge(&sk->sk_error_queue);
break;
case IP_MULTICAST_TTL:
if (sk->sk_type == SOCK_STREAM)
goto e_inval;
if (optlen<1)
goto e_inval;
if (val==-1)
val = 1;
if (val < 0 || val > 255)
goto e_inval;
inet->mc_ttl = val;
break;
case IP_MULTICAST_LOOP:
if (optlen<1)
94
goto e_inval;
inet->mc_loop = !!val;
break;
case IP_MULTICAST_IF:
{
struct ip_mreqn mreq;
struct net_device *dev = NULL;
if (sk->sk_type == SOCK_STREAM)
goto e_inval;
/*
* Check the arguments are allowable
*/
err = -EFAULT;
if (optlen >= sizeof(struct ip_mreqn)) {
if (copy_from_user(&mreq,optval,sizeof(mreq)))
break;
} else {
memset(&mreq, 0, sizeof(mreq));
if (optlen >= sizeof(struct in_addr) &&
copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
break;
}
if (!mreq.imr_ifindex) {
if (mreq.imr_address.s_addr == INADDR_ANY) {
inet->mc_index = 0;
inet->mc_addr = 0;
err = 0;
break;
}
dev = ip_dev_find(mreq.imr_address.s_addr);
if (dev) {
mreq.imr_ifindex = dev->ifindex;
dev_put(dev);
}
} else
dev = __dev_get_by_index(mreq.imr_ifindex);
err = -EADDRNOTAVAIL;
if (!dev)
break;
95
err = -EINVAL;
if (sk->sk_bound_dev_if &&
mreq.imr_ifindex != sk->sk_bound_dev_if)
break;
inet->mc_index = mreq.imr_ifindex;
inet->mc_addr = mreq.imr_address.s_addr;
err = 0;
break;
}
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
{
struct ip_mreqn mreq;
if (optlen < sizeof(struct ip_mreq))
goto e_inval;
err = -EFAULT;
if (optlen >= sizeof(struct ip_mreqn)) {
if(copy_from_user(&mreq,optval,sizeof(mreq)))
break;
} else {
memset(&mreq, 0, sizeof(mreq));
if (copy_from_user(&mreq,optval,sizeof(struct
ip_mreq)))
break;
}
if (optname == IP_ADD_MEMBERSHIP)
err = ip_mc_join_group(sk, &mreq);
else
err = ip_mc_leave_group(sk, &mreq);
break;
}
case IP_MSFILTER:
{
extern int sysctl_optmem_max;
extern int sysctl_igmp_max_msf;
struct ip_msfilter *msf;
if (optlen < IP_MSFILTER_SIZE(0))
goto e_inval;
if (optlen > sysctl_optmem_max) {
err = -ENOBUFS;
break;
96
}
msf = (struct ip_msfilter *)kmalloc(optlen, GFP_KERNEL);
if (msf == 0) {
err = -ENOBUFS;
break;
}
err = -EFAULT;
if (copy_from_user(msf, optval, optlen)) {
kfree(msf);
break;
}
/* numsrc >= (1G-4) overflow in 32 bits */
if (msf->imsf_numsrc >= 0x3ffffffcU ||
msf->imsf_numsrc > sysctl_igmp_max_msf) {
kfree(msf);
err = -ENOBUFS;
break;
}
if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
kfree(msf);
err = -EINVAL;
break;
}
err = ip_mc_msfilter(sk, msf, 0);
kfree(msf);
break;
}
case IP_BLOCK_SOURCE:
case IP_UNBLOCK_SOURCE:
case IP_ADD_SOURCE_MEMBERSHIP:
case IP_DROP_SOURCE_MEMBERSHIP:
{
struct ip_mreq_source mreqs;
int omode, add;
if (optlen != sizeof(struct ip_mreq_source))
goto e_inval;
if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
err = -EFAULT;
break;
}
if (optname == IP_BLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 1;
} else if (optname == IP_UNBLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
97
add = 0;
} else if (optname == IP_ADD_SOURCE_MEMBERSHIP)
{
struct ip_mreqn mreq;
mreq.imr_multiaddr.s_addr =
mreqs.imr_multiaddr;
mreq.imr_address.s_addr = mreqs.imr_interface;
mreq.imr_ifindex = 0;
err = ip_mc_join_group(sk, &mreq);
if (err)
break;
omode = MCAST_INCLUDE;
add = 1;
} else /*IP_DROP_SOURCE_MEMBERSHIP */ {
omode = MCAST_INCLUDE;
add = 0;
}
err = ip_mc_source(add, omode, sk, &mreqs, 0);
break;
}
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
{
struct group_req greq;
struct sockaddr_in *psin;
struct ip_mreqn mreq;
if (optlen < sizeof(struct group_req))
goto e_inval;
err = -EFAULT;
if(copy_from_user(&greq, optval, sizeof(greq)))
break;
psin = (struct sockaddr_in *)&greq.gr_group;
if (psin->sin_family != AF_INET)
goto e_inval;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr = psin->sin_addr;
mreq.imr_ifindex = greq.gr_interface;
if (optname == MCAST_JOIN_GROUP)
err = ip_mc_join_group(sk, &mreq);
else
err = ip_mc_leave_group(sk, &mreq);
break;
}
98
case MCAST_JOIN_SOURCE_GROUP:
case MCAST_LEAVE_SOURCE_GROUP:
case MCAST_BLOCK_SOURCE:
case MCAST_UNBLOCK_SOURCE:
{
struct group_source_req greqs;
struct ip_mreq_source mreqs;
struct sockaddr_in *psin;
int omode, add;
if (optlen != sizeof(struct group_source_req))
goto e_inval;
if (copy_from_user(&greqs, optval, sizeof(greqs))) {
err = -EFAULT;
break;
}
if (greqs.gsr_group.ss_family != AF_INET ||
greqs.gsr_source.ss_family != AF_INET) {
err = -EADDRNOTAVAIL;
break;
}
psin = (struct sockaddr_in *)&greqs.gsr_group;
mreqs.imr_multiaddr = psin->sin_addr.s_addr;
psin = (struct sockaddr_in *)&greqs.gsr_source;
mreqs.imr_sourceaddr = psin->sin_addr.s_addr;
mreqs.imr_interface = 0; /* use index for mc_source */
if (optname == MCAST_BLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 1;
} else if (optname == MCAST_UNBLOCK_SOURCE) {
omode = MCAST_EXCLUDE;
add = 0;
} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
struct ip_mreqn mreq;
psin = (struct sockaddr_in *)&greqs.gsr_group;
mreq.imr_multiaddr = psin->sin_addr;
mreq.imr_address.s_addr = 0;
mreq.imr_ifindex = greqs.gsr_interface;
err = ip_mc_join_group(sk, &mreq);
if (err)
break;
greqs.gsr_interface = mreq.imr_ifindex;
omode = MCAST_INCLUDE;
add = 1;
99
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
omode = MCAST_INCLUDE;
add = 0;
}
err = ip_mc_source(add, omode, sk, &mreqs,
greqs.gsr_interface);
break;
}
case MCAST_MSFILTER:
{
extern int sysctl_optmem_max;
extern int sysctl_igmp_max_msf;
struct sockaddr_in *psin;
struct ip_msfilter *msf = NULL;
struct group_filter *gsf = NULL;
int msize, i, ifindex;
if (optlen < GROUP_FILTER_SIZE(0))
goto e_inval;
if (optlen > sysctl_optmem_max) {
err = -ENOBUFS;
break;
}
gsf = (struct group_filter *)kmalloc(optlen,GFP_KERNEL);
if (gsf == 0) {
err = -ENOBUFS;
break;
}
err = -EFAULT;
if (copy_from_user(gsf, optval, optlen)) {
goto mc_msf_out;
}
/* numsrc >= (4G-140)/128 overflow in 32 bits */
if (gsf->gf_numsrc >= 0x1ffffff ||
gsf->gf_numsrc > sysctl_igmp_max_msf) {
err = -ENOBUFS;
goto mc_msf_out;
}
if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
err = -EINVAL;
goto mc_msf_out;
}
msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
msf = (struct ip_msfilter *)kmalloc(msize,GFP_KERNEL);
if (msf == 0) {
err = -ENOBUFS;
100
goto mc_msf_out;
}
ifindex = gsf->gf_interface;
psin = (struct sockaddr_in *)&gsf->gf_group;
if (psin->sin_family != AF_INET) {
err = -EADDRNOTAVAIL;
goto mc_msf_out;
}
msf->imsf_multiaddr = psin->sin_addr.s_addr;
msf->imsf_interface = 0;
msf->imsf_fmode = gsf->gf_fmode;
msf->imsf_numsrc = gsf->gf_numsrc;
err = -EADDRNOTAVAIL;
for (i=0; i<gsf->gf_numsrc; ++i) {
psin = (struct sockaddr_in *)&gsf->gf_slist[i];
if (psin->sin_family != AF_INET)
goto mc_msf_out;
msf->imsf_slist[i] = psin->sin_addr.s_addr;
}
kfree(gsf);
gsf = NULL;
err = ip_mc_msfilter(sk, msf, ifindex);
mc_msf_out:
if (msf)
kfree(msf);
if (gsf)
kfree(gsf);
break;
}
case IP_ROUTER_ALERT:
err = ip_ra_control(sk, val ? 1 : 0, NULL);
break;
case IP_FREEBIND:
if (optlen<1)
goto e_inval;
inet->freebind = !!val;
break;
case IP_IPSEC_POLICY:
case IP_XFRM_POLICY:
err = xfrm_user_policy(sk, optname, optval, optlen);
break;
101
default:
#ifdef CONFIG_NETFILTER
err = nf_setsockopt(sk, PF_INET, optname, optval,
optlen);
#else
err = -ENOPROTOOPT;
#endif
break;
}
release_sock(sk);
return err;
e_inval:
release_sock(sk);
return -EINVAL;
}
/*
* Get the options. Note for future reference. The GET of IP options gets the
* _received_ ones. The set sets the _sent_ ones.
*/
int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user
*optlen)
{
struct inet_opt *inet = inet_sk(sk);
int val;
int len;
if(level!=SOL_IP)
return -EOPNOTSUPP;
#ifdef CONFIG_IP_MROUTE
if(optname>=MRT_BASE && optname <=MRT_BASE+10)
{
return ip_mroute_getsockopt(sk,optname,optval,optlen);
}
#endif
if(get_user(len,optlen))
return -EFAULT;
if(len < 0)
return -EINVAL;
lock_sock(sk);
102
switch(optname) {
case IP_OPTIONS:
{
unsigned char optbuf[sizeof(struct ip_options)+40];
struct ip_options * opt = (struct
ip_options*)optbuf;
opt->optlen = 0;
if (inet->opt)
memcpy(optbuf, inet->opt,
sizeof(struct ip_options)+
inet->opt->optlen);
release_sock(sk);
if (opt->optlen == 0)
return put_user(0, optlen);
ip_options_undo(opt);
len = min_t(unsigned int, len, opt->optlen);
if(put_user(len, optlen))
return -EFAULT;
if(copy_to_user(optval, opt->__data, len))
return -EFAULT;
return 0;
}
case IP_PKTINFO:
val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0;
break;
case IP_RECVTTL:
val = (inet->cmsg_flags & IP_CMSG_TTL) != 0;
break;
case IP_RECVTOS:
val = (inet->cmsg_flags & IP_CMSG_TOS) != 0;
break;
case IP_RECVOPTS:
val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0;
break;
case IP_RETOPTS:
val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
break;
case IP_TOS:
val = inet->tos;
break;
case IP_TTL:
val = (inet->uc_ttl == -1 ?
sysctl_ip_default_ttl :
103
inet->uc_ttl);
break;
case IP_HDRINCL:
val = inet->hdrincl;
break;
case IP_MTU_DISCOVER:
val = inet->pmtudisc;
break;
case IP_MTU:
{
struct dst_entry *dst;
val = 0;
dst = sk_dst_get(sk);
if (dst) {
val = dst_pmtu(dst) - dst->header_len;
dst_release(dst);
}
if (!val) {
release_sock(sk);
return -ENOTCONN;
}
break;
}
case IP_RECVERR:
val = inet->recverr;
break;
case IP_MULTICAST_TTL:
val = inet->mc_ttl;
break;
case IP_MULTICAST_LOOP:
val = inet->mc_loop;
break;
case IP_MULTICAST_IF:
{
struct in_addr addr;
len = min_t(unsigned int, len, sizeof(struct in_addr));
addr.s_addr = inet->mc_addr;
release_sock(sk);
if(put_user(len, optlen))
return -EFAULT;
if(copy_to_user(optval, &addr, len))
return -EFAULT;
return 0;
}
case IP_MSFILTER:
104
{
struct ip_msfilter msf;
int err;
if (len < IP_MSFILTER_SIZE(0)) {
release_sock(sk);
return -EINVAL;
}
if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0)))
{
release_sock(sk);
return -EFAULT;
}
err = ip_mc_msfget(sk, &msf,
(struct ip_msfilter __user *)optval, optlen);
release_sock(sk);
return err;
}
case MCAST_MSFILTER:
{
struct group_filter gsf;
int err;
if (len < GROUP_FILTER_SIZE(0)) {
release_sock(sk);
return -EINVAL;
}
if (copy_from_user(&gsf, optval,
GROUP_FILTER_SIZE(0))) {
release_sock(sk);
return -EFAULT;
}
err = ip_mc_gsfget(sk, &gsf,
(struct group_filter __user *)optval, optlen);
release_sock(sk);
return err;
}
case IP_PKTOPTIONS:
{
struct msghdr msg;
release_sock(sk);
if (sk->sk_type != SOCK_STREAM)
return -ENOPROTOOPT;
105
msg.msg_control = optval;
msg.msg_controllen = len;
msg.msg_flags = 0;
if (inet->cmsg_flags & IP_CMSG_PKTINFO) {
struct in_pktinfo info;
info.ipi_addr.s_addr = inet->rcv_saddr;
info.ipi_spec_dst.s_addr = inet->rcv_saddr;
info.ipi_ifindex = inet->mc_index;
put_cmsg(&msg, SOL_IP, IP_PKTINFO,
sizeof(info), &info);
}
if (inet->cmsg_flags & IP_CMSG_TTL) {
int hlim = inet->mc_ttl;
put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim),
&hlim);
}
len -= msg.msg_controllen;
return put_user(len, optlen);
}
case IP_FREEBIND:
val = inet->freebind;
break;
#ifdef CONFIG_IP_RTP_NAT
case IP_RTP_NAT_SET_DATA:
{
int err = 0;
if (len == (2 * sizeof (unsigned int)))
{
unsigned int rtpnat_ips[2];
/* Got entities IP addresses from userspace */
if (copy_from_user (&rtpnat_ips, optval, len))
{
err = -EFAULT;
break;
}
if (rtpnat_set_ent (rtpnat_ips[0], rtpnat_ips[1]))
err = -EFAULT;
}
else if (len == sizeof (struct session_entry))
{
106
struct session_entry session;
/* Got session from userspace */
if (copy_from_user (&session, optval, len))
{
err = -EFAULT;
break;
}
session.nat.assoc_port =
ntohs(rtpnat_session_registrar (&session));
session.nat.dest_ip = ntohl(session.nat.dest_ip);
if (ERRONEOUS_PORT ==
session.nat.assoc_port)
{
err = -EFAULT;
break;
}
/* Returns associated NAT port via nat.source_port
to the userspace */
if (put_user (len, optlen))
{
err = -EFAULT;
break;
}
if (copy_to_user (optval, &session, len))
{
err = -EFAULT;
break;
}
}
else if (len == sizeof (int))
{
unsigned int cmd;
unsigned long expire_time = 5; /* Default exp.
time */
/* Got CMD from userspace */
if (copy_from_user (&cmd, optval, len))
{
err = -EFAULT;
break;
}
107
if (cmd & IP_RTP_NAT_SET_TOUT)
{
expire_time = (cmd &
~(IP_RTP_NAT_SET_TOUT));
cmd = IP_RTP_NAT_SET_TOUT;
}
switch (cmd)
{
case IP_RTP_NAT_FLUSH:
rtpnat_flush();
break;
case IP_RTP_NAT_ACTIVE_PAIRS:
cmd = rtpnat_get_pairs();
if (put_user (len, optlen))
{
err = -EFAULT;
break;
}
if (copy_to_user (optval, &cmd,
len))
{
err = -EFAULT;
break;
}
break;
case IP_RTP_NAT_SET_TOUT:
rtpnat_set_expiration
(expire_time);
break;
default:
err = -EINVAL;
}
}
else if (len == sizeof (struct deny_pair))
{
struct deny_pair dp;
unsigned int interr;
/* Got deny pair from userspace */
if (copy_from_user (&dp, optval, len))
{
err = -EFAULT;
break;
}
108
/* Execute denial rule */
interr = rtpnat_deny_pair (dp.src_ip, dp.dst_port);
if (interr)
err = -EINVAL;
}
else if (len == sizeof (struct timedout_buffer_usr))
{
struct timedout_buffer_usr tbuf;
if (rtpnat_get_tout_buffer (&tbuf))
{
err = -EINVAL;
break;
}
if (copy_to_user (optval, &tbuf, len))
{
err = -EFAULT;
break;
}
}
else
err = -EINVAL;
release_sock (sk);
return err;
}
#endif /* CONFIG_IP_RTP_NAT */
default:
#ifdef CONFIG_NETFILTER
val = nf_getsockopt(sk, PF_INET, optname, optval,
&len);
release_sock(sk);
if (val >= 0)
val = put_user(len, optlen);
return val;
#else
release_sock(sk);
return -ENOPROTOOPT;
#endif
}
release_sock(sk);
if (len < sizeof(int) && len > 0 && val>=0 && val<255) {
109
unsigned char ucval = (unsigned char)val;
len = 1;
if(put_user(len, optlen))
return -EFAULT;
if(copy_to_user(optval,&ucval,1))
return -EFAULT;
} else {
len = min_t(unsigned int, sizeof(int), len);
if(put_user(len, optlen))
return -EFAULT;
if(copy_to_user(optval,&val,len))
return -EFAULT;
}
return 0;
}
EXPORT_SYMBOL(ip_cmsg_recv);
#ifdef CONFIG_IP_SCTP_MODULE
EXPORT_SYMBOL(ip_getsockopt);
EXPORT_SYMBOL(ip_setsockopt);
#endif
110
6. linux/net/ipv4/rtpnat.c
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/skbuff.h>
#include <linux/rtpnat.h>
#include <linux/etherdevice.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/checksum.h>
/* NAT session timeout = 5 sec */
#define NAT_TIMEOUT nat_timeout
/* ===========================================
Data structures.
=========================================== */
/* NAT entries timeout variable */
static unsigned long nat_timeout;
/* Entites Data - tables, indexed by ingress packets' source port */
static struct nat_entry portIDX[ENTITIES][ENT_SIZE];
/*
* Linked list and their representatives as follows:
* 1. Sessions. Connects two ports in both entities by session ID.
* 2. Active ports - Used ports within entities. Should expire after some time.
*/
static struct ll_list sessions_list, active_list[ENTITIES];
static struct ll_list *sessions, *active[ENTITIES];
/* Available (non-active) ports */
static unsigned short *available[ENTITIES];
/* Entity mask. Computation, which gives as a result entity number, based on IP address
*/
static unsigned int ent_mask;
111
/* IP addresses of the entities. SHOULD COME FROM setsockopt() !!! */
static unsigned int set_ent_ip[ENTITIES];
/* Entities as network devices */
static struct net_device *ent_devs[ENTITIES];
/* Active NAT pairs */
static unsigned int active_pairs;
/* Timed-out session entries buffer + counter */
static struct timedout_buffer tout_sessions;
#ifdef RTPNAT_STATS
/* Used for statistics */
static unsigned long cntr__ = 0, tm__ = 0;
#endif
/* ===========================================
Local function declarations.
=========================================== */
static unsigned short update_nat_table (struct pre_nat_entry *nat);
static void activate_nat_pair (struct pre_nat_entry *nat1, struct
pre_nat_entry *nat2);
static unsigned int extract_entity (unsigned int ip);
static unsigned short get_port (unsigned int entity);
static void check_expired (void);
/* ===========================================
Common data structure handling functions.
=========================================== */
/* Adds an entry to llist's tail */
static void ll_add_entry_tail (struct ll_list *list, void *data)
{
struct ll_entry *entry;
#ifdef RTPNAT_DEBUG
if (!list)
{
printk ("%s: List doesn't exists!\n", __FUNCTION__);
return;
}
if (!data)
112
{
printk ("%s: No data to add!\n", __FUNCTION__);
return;
}
#endif
/* Allocate memory for the entry and attach the data structure */
entry = kmalloc (sizeof (struct ll_entry), GFP_KERNEL);
#ifdef RTPNAT_DEBUG
if (!entry)
{
printk ("%s: No available memory for the entry!\n",
__FUNCTION__);
return;
}
#endif
entry->data = data;
/* Do some link attaching stuff */
if (!list->tail)
{
entry->prev = entry->next = NULL;
list->head = list->tail = entry;
}
else
{
list->tail->next = entry;
entry->prev = list->tail;
entry->next = NULL;
list->tail = entry;
}
}
#if 0
/* Adds an entry to llist's head */
static void ll_add_entry_head (struct ll_list *list, void *data)
{
struct ll_entry *entry;
#ifdef RTPNAT_DEBUG
if (!list)
{
printk ("%s: List doesn't exists!\n", __FUNCTION__);
return;
}
113
if (!data)
{
printk ("%s: No data to add!\n", __FUNCTION__);
return;
}
#endif
/* Allocate memory for the entry and attach the data structure */
entry = kmalloc (sizeof (struct ll_entry), GFP_KERNEL);
#ifdef RTPNAT_DEBUG
if (!entry)
{
printk ("%s: No available memory for the entry!\n",
__FUNCTION__);
return;
}
#endif
entry->data = data;
/* Do some link attaching stuff */
if (!list->head)
{
entry->prev = entry->next = NULL;
list->head = list->tail = entry;
}
else
{
list->head->prev = entry;
entry->next = list->head;
entry->prev = NULL;
list->head = entry;
}
}
#endif /* 0 */
/* Removes an entry from llist's head */
static void ll_remove_entry (struct ll_list *list, struct ll_entry *entry)
{
#ifdef RTPNAT_DEBUG
if (!list)
{
printk ("%s: List doesn't exists!\n", __FUNCTION__);
return;
}
if (!entry)
114
{
printk ("%s: Entry, belongs to list %p, doesn't exists!\n",
__FUNCTION__, list);
return;
}
if (!list->head || !list->tail)
{
printk ("%s: List %p has no head and/or tail!\n", __FUNCTION__,
list);
return;
}
#endif
/* Do some link removing stuff */
if (entry == list->head)
list->head = entry->next;
if (entry == list->tail)
list->tail = entry->prev;
if (entry->prev)
entry->prev->next = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
/* Now release the entry */
kfree (entry);
}
/* Memory reallocation function, needed for timed-out sessions rebuffering */
static void *kmem_realloc (void *ptr, size_t newsize, size_t oldsize, int flags)
{
void *new;
new = kmalloc (newsize, flags);
if (ptr)
{
if (new)
memcpy (new, ptr, ((oldsize < newsize)?oldsize:newsize));
kfree (ptr);
}
return new;
}
/* ===========================================
Local functions.
115
=========================================== */
/* Timed-out sessions buffer resizing (if needed) */
static void inline tout_buff_inc (void)
{
if (++tout_sessions.len == tout_sessions.max_len)
{
tout_sessions.max_len += IP_RTP_TOUT_BUFFER_RESIZE;
tout_sessions.buffer = (struct timedout_session *) kmem_realloc
(tout_sessions.buffer, tout_sessions.max_len, tout_sessions.len, GFP_KERNEL);
#ifdef RTPNAT_DEBUG
if (!tout_sessions.buffer)
printk ("%s: No available memory for timed out sessions
table!\n", __FUNCTION__);
#endif
}
}
/* Timed-out session adding */
static void tout_add_entry (struct nat_entry *entry)
{
/* Timed-out sessions buffer resizing (if needed) */
tout_buff_inc ();
/* Add data */
tout_sessions.buffer[tout_sessions.len - 1].ip = entry -> source_ip;
tout_sessions.buffer[tout_sessions.len - 1].port = entry -> assoc_port;
tout_sessions.buffer[tout_sessions.len - 1].expired = jiffies;
}
/* Updates NAT table per device */
static unsigned short update_nat_table (struct pre_nat_entry *nat)
{
unsigned int dev_entity;
unsigned short assoc_port;
struct nat_entry *entry;
/* Check for proper entities configuration */
if ((htonl(nat->dest_ip) != set_ent_ip[DEV_ORIGIN] && htonl(nat->dest_ip)
!= set_ent_ip[DEV_TERM]) || !set_ent_ip[DEV_ORIGIN] || !set_ent_ip[DEV_TERM])
{
printk ("RTPNAT: Given by getsockopt() IP is neither correct IP
interfaces' address, nor RTPNAT is not initialized with such addresses\n");
return ERRONEOUS_PORT;
}
if (set_ent_ip[DEV_ORIGIN] == set_ent_ip[DEV_TERM])
116
{
printk ("RTPNAT: IP addresses of both interfaces are one and the
same!\n");
return ERRONEOUS_PORT;
}
/* Extract entity number */
dev_entity = extract_entity (htonl(nat->dest_ip));
/* Get free port */
assoc_port = get_port (dev_entity);
#ifdef RTPNAT_DEBUG
printk ("%s: Associating port %u to %u for Source IP %8.8x\n",
__FUNCTION__, nat->source_port, ntohs(assoc_port), nat->source_ip);
#endif
/* Sign inactive NAT entry */
entry = &portIDX[dev_entity][assoc_port];
entry->source_ip = htonl(nat->source_ip);
entry->source_port = htons(nat->source_port);
entry->assoc_port = assoc_port;
entry->active = NAT_INACTIVE;
entry->link = ERRONEOUS_PORT;
/* Attach associated port to preNAT entry */
nat->assoc_port = assoc_port;
nat->dest_ip = htonl(nat->dest_ip);
return assoc_port;
}
/* Actitvates NAT table pair and attaches timer */
static void activate_nat_pair (struct pre_nat_entry *nat1, struct pre_nat_entry *nat2)
{
unsigned int dev_entity1, dev_entity2;
struct nat_entry *entry1, *entry2;
/* Extract entities numbers */
dev_entity1 = extract_entity (nat1->dest_ip);
dev_entity2 = extract_entity (nat2->dest_ip);
/* Activate NAT entries */
entry1 = &portIDX[dev_entity1][nat1->assoc_port];
entry2 = &portIDX[dev_entity2][nat2->assoc_port];
/* If entries are equal, we have serious error! */
if (dev_entity1 == dev_entity2)
117
{
printk ("RTPNAT: NAT session with same ends - %8.8x and
%8.8x\n", ntohl(nat1->dest_ip), ntohl(nat2->dest_ip));
}
else
{
entry1->active = entry2->active = NAT_ACTIVE;
entry1->link = nat2->assoc_port;
entry2->link = nat1->assoc_port;
}
entry1->last = entry2->last = jiffies; /* jiffies = NOW */
/* Increment active sessions */
active_pairs++;
/* Put active entries in active ports lists */
ll_add_entry_tail (active[dev_entity1], (void *)entry1);
ll_add_entry_tail (active[dev_entity2], (void *)entry2);
}
/* Extract entity number per desination IP */
static unsigned int extract_entity (unsigned int ip)
{
if (!ent_mask)
{
unsigned int shifted_left;
unsigned int uniq_bits;
/* XOR between two different IP addresses will give us proper mask
*/
for (shifted_left = 0, uniq_bits = set_ent_ip[DEV_ORIGIN] ^
set_ent_ip[DEV_TERM]; (!(uniq_bits & 1)); uniq_bits >>= 1)
shifted_left++;
ent_mask = 1 << shifted_left;
#ifdef RTPNAT_DEBUG
printk ("%s: Entity mask = %8.8x\nDEV_ORIGIN & MASK =
%8.8x\nDEV_TERM & MASK = %8.8x\n", __FUNCTION__, ent_mask,
set_ent_ip[DEV_ORIGIN] & ent_mask, set_ent_ip[DEV_TERM] & ent_mask);
#endif
}
return !!(ip & ent_mask);
}
118
/* Get free port from associated interface */
static unsigned short get_port (unsigned int entity)
{
/* Gets current free port and moves table pointer to the next */
return *(available[entity]++);
}
/* Check for expired NAT sessions */
static void check_expired (void)
{
unsigned int ents;
unsigned short assoc_port;
struct ll_entry *entry, *next;
struct nat_entry *data; /* Do not free! Associated to staticaly allocated memory!
*/
unsigned int inactive_atoms = 0; /* Inactive ends of a pair */
/* Process all entities's active ports */
for (ents = DEV_ORIGIN; ents <= DEV_TERM; ents++)
{
/* Process entity active ports */
entry = active[ents]->head;
while (entry)
{
data = (struct nat_entry *)entry->data;
/* Check for timeouted NAT session */
if ((jiffies - data->last) > NAT_TIMEOUT)
{
#ifdef RTPNAT_DEBUG
printk ("%s: Timeouted NAT session for origin IP
%8.8x:%u, associated NAT port %u\n", __FUNCTION__, ntohl(data->source_ip),
ntohs(data->source_port), ntohs(data->assoc_port));
#endif
/* Add timed-out entry data for Asterisk-side
expired entries */
if (ents == DEV_TERM)
tout_add_entry (data);
/* Deactivate NAT session */
data->active = NAT_INACTIVE;
/* For sync purpose - first delete from active port
list, then add to available ports. We need to keep assoc_port and next entry */
assoc_port = data->assoc_port;
next = entry->next;
/* Delete entry */
ll_remove_entry (active[ents], entry);
119
/* Return associated port to available ports */
*(--available[ents]) = assoc_port;
/* Decrement active sessions */
inactive_atoms++;
/* Next entry */
entry = next;
}
else
entry = entry->next;
}
}
/* Active pairs are decremented with inactive ends, divided by 2 */
#ifdef __LITTLE_ENDIAN
active_pairs -= (inactive_atoms >> 1);
#else
active_pairs -= (inactive_atoms / 2);
#endif
}
/* ===========================================
Global functions.
=========================================== */
/* Registers Entities addresses */
unsigned int rtpnat_set_ent (unsigned int ent_ip1, unsigned int ent_ip2)
{
if (set_ent_ip[DEV_ORIGIN] || set_ent_ip[DEV_TERM])
{
printk ("RTPNAT: Entity IP addresses already set!\n");
return 1;
}
else
{
struct net_device *cdev;
/* Interfaces loop */
for (cdev = dev_base; cdev; cdev = cdev->next)
{
struct in_ifaddr *ifaddr;
if (set_ent_ip[DEV_ORIGIN] && set_ent_ip[DEV_TERM])
return 0;
if (!cdev->ip_ptr)
continue;
120
if (!((struct in_device *)(cdev->ip_ptr))->ifa_list)
continue;
/* IP interfaces loop */
for (ifaddr = ((struct in_device *)(cdev->ip_ptr))->ifa_list;
ifaddr; ifaddr = ifaddr->ifa_next)
{
if (ifaddr->ifa_address == htonl(ent_ip1))
{
set_ent_ip[DEV_ORIGIN] =
htonl(ent_ip1);
ent_devs[DEV_ORIGIN] = cdev;
#ifdef RTPNAT_DEBUG
printk ("%s: Setting ORIG entity %s IP
addresses %8.8x, device %s\n", __FUNCTION__, ifaddr->ifa_label, ent_ip1, cdev-
>name);
#endif
}
else if (ifaddr->ifa_address == htonl(ent_ip2))
{
set_ent_ip[DEV_TERM] =
htonl(ent_ip2);
ent_devs[DEV_TERM] = cdev;
#ifdef RTPNAT_DEBUG
printk ("%s: Setting TERM entity %s IP
addresses %8.8x, device %s\n", __FUNCTION__, ifaddr->ifa_label, ent_ip2, cdev-
>name);
#endif
}
}
}
if (set_ent_ip[DEV_ORIGIN] && set_ent_ip[DEV_TERM])
return 0;
set_ent_ip[DEV_ORIGIN] = set_ent_ip[DEV_TERM] = 0;
printk ("RTPNAT: %8.8x and/or %8.8x are incorrect IP addresses to
related IP netdevs!\n", ent_ip1, ent_ip2);
return 1;
}
}
/* Registers/updates new session to the NAT. Returns NAT port. */
unsigned short rtpnat_session_registrar (struct session_entry *sentry)
{
struct pre_nat_entry *nentry;
121
struct ll_entry *entry;
struct session_entry *data;
unsigned short nat_port;
/* Pre-NAT data of the session */
nentry = (struct pre_nat_entry *)&sentry->nat;
/* Check for expired NAT sessions */
check_expired();
/* Session match lookup */
for (entry = sessions->head; entry; entry = entry->next)
if (((struct session_entry *)(entry->data))->session_len == sentry-
>session_len)
if (!memcmp(((struct session_entry *)(entry->data))-
>session_id, sentry->session_id, sentry->session_len))
{
#ifdef RTPNAT_DEBUG
printk ("%s: Session match found - [%s]:[%s]\n",
__FUNCTION__, ((struct session_entry *)(entry->data))->session_id, sentry-
>session_id);
#endif
/* Update NAT table */
nat_port = update_nat_table (nentry);
if (ERRONEOUS_PORT == nat_port)
return ERRONEOUS_PORT;
/* Activate both entries in NAT tables */
activate_nat_pair (nentry, (struct pre_nat_entry
*)&((struct session_entry *)(entry->data))->nat);
/* Free session data */
kfree (entry->data);
/* Remove LL entry */
ll_remove_entry (sessions, entry);
return nat_port;
}
/* Session not found, just add another */
#ifdef RTPNAT_DEBUG
printk ("%s: Session match not found per [%s]\n", __FUNCTION__, sentry-
>session_id);
#endif
nat_port = update_nat_table (nentry);
if (ERRONEOUS_PORT == nat_port)
return ERRONEOUS_PORT;
122
data = kmalloc (sizeof (struct session_entry) , GFP_KERNEL);
#ifdef RTPNAT_DEBUG
if (!data)
{
printk ("%s: No available memory for the data!\n", __FUNCTION__);
return ERRONEOUS_PORT;
}
#endif
memcpy (data, sentry, sizeof (struct session_entry));
ll_add_entry_tail (sessions, (void *)data);
return nat_port;
}
/* ZeroCopy function. Do checks, do routing, do sending */
int rtpnat_process_skb (struct sk_buff *skb, struct net_device *dev)
{
unsigned int ingress_direction, egress_direction;
struct nat_entry *ingress_nat, *egress_nat;
struct iphdr *iph = skb->nh.iph;
struct udphdr *uh;
unsigned int ihl_size;
struct dst_entry *dst;
struct hh_cache *hh;
struct net_device *outdev;
int hh_len;
/* Device match -> detect directions */
if (dev != ent_devs[DEV_ORIGIN])
{
if (dev != ent_devs[DEV_TERM])
{
return 1;
}
else
{
ingress_direction = DEV_TERM;
egress_direction = DEV_ORIGIN;
}
}
else
{
ingress_direction = DEV_ORIGIN;
egress_direction = DEV_TERM;
123
}
/* If packet is fragmented - defragment it */
if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
skb = ip_defrag(skb);
if (!skb)
return 0;
iph = skb->nh.iph;
ip_send_check(iph);
}
/* Packet length check */
ihl_size = iph->ihl * 4; /* Size in words */
if (ihl_size + sizeof (*uh) > skb->len)
return 1;
/* Protocol match */
if (iph->protocol != IPPROTO_UDP)
return 1;
else
uh = (struct udphdr *)((char *) iph + ihl_size);
#ifdef RTPNAT_DEBUG
printk ("%s: Origin %8.8x, Term %8.8x, UDP packet OK\n", __FUNCTION__,
ntohl(set_ent_ip[ingress_direction]), ntohl(set_ent_ip[egress_direction]));
#endif
/* Find the entry in portIDX table */
ingress_nat = &portIDX[ingress_direction][uh->dest];
egress_nat = &portIDX[egress_direction][ingress_nat->link];
#ifdef RTPNAT_DEBUG
printk ("%s: SourcePort %u, NAT entry timer %lu, %s, %s, Address check -
%8.8x versus %8.8x\n", __FUNCTION__, ntohs(uh->source), (jiffies - egress_nat-
>last)/HZ, (egress_nat->active == NAT_INACTIVE)?"NAT inactive":"NAT active",
(EXCEPTION_PORT(ntohs(uh->source)))?"Exception port":"Allowed port", ntohl(iph-
>saddr), ntohl(egress_nat->source_ip));
#endif
/* Exception ports shouldn't pass */
if (EXCEPTION_PORT(ntohs(uh->source)))
return 1;
/* Timed out on inactive NAT pair */
if (jiffies - egress_nat->last > NAT_TIMEOUT || egress_nat->active ==
NAT_INACTIVE ||
124
jiffies - ingress_nat->last > NAT_TIMEOUT || ingress_nat->active ==
NAT_INACTIVE)
return 1;
/* Wrong source IP address or UDP source port */
if (iph->saddr != egress_nat->source_ip || uh->source != egress_nat-
>source_port)
return 1;
#ifdef RTPNAT_DEBUG
printk ("%s: NAT entry exists, continue with zercopy\n", __FUNCTION__);
printk ("%s: Replacing %8.8x:%u with %8.8x:%u\n", __FUNCTION__,
ntohl(iph->daddr), ntohs(uh->dest), ntohl(ingress_nat->source_ip), ntohs(egress_nat-
>link));
#endif
/* Prerouting */
iph->daddr = ingress_nat->source_ip;
uh->dest = ingress_nat->source_port;
/* Fix checksums */
ip_send_check(iph);
uh->check = 0;
uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - ihl_size,
IPPROTO_UDP, csum_partial((char *)uh, skb->len - ihl_size, 0));
/* Routing */
if (skb->dst == NULL)
if(ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
return 2;
#ifdef RTPNAT_DEBUG
printk ("%s: Routing passed, process postrouting\n", __FUNCTION__);
#endif
/* Postrouting */
iph->saddr = set_ent_ip[egress_direction];
uh->source = egress_nat->assoc_port;
/* Fix checksums */
ip_send_check(iph);
uh->check = 0;
uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - ihl_size,
IPPROTO_UDP, csum_partial((char *)uh, skb->len - ihl_size, 0));
/* Update timings */
egress_nat->last = jiffies;
125
#ifdef RTPNAT_DEBUG
printk ("%s: NATed packet %8.8x:%u/%8.8x:%u\n", __FUNCTION__,
ntohl(iph->saddr), ntohs(uh->source), ntohl(iph->daddr), ntohs(uh->dest));
#endif
/* Now get the packet out */
skb->dev = skb->dst->dev;
skb->protocol = htons(ETH_P_IP);
dst = skb->dst;
hh = dst->hh;
outdev = dst->dev;
hh_len = LL_RESERVED_SPACE(outdev);
if (unlikely(skb_headroom(skb) < hh_len && outdev->hard_header)) {
struct sk_buff *skb2;
skb2 = skb_realloc_headroom(skb,
LL_RESERVED_SPACE(outdev));
if (skb2 == NULL) {
kfree_skb(skb);
return 2;
}
if (skb->sk)
skb_set_owner_w(skb2, skb->sk);
kfree_skb(skb);
skb = skb2;
}
#ifdef RTPNAT_STATS
if (!tm__ || (jiffies - tm__ >= HZ))
{
printk ("Pkt per sec - %lu\n", cntr__);
tm__ = jiffies;
cntr__ = 0;
}
else
cntr__++;
#endif
if (hh) {
int hh_alen;
read_lock_bh(&hh->hh_lock);
hh_alen = HH_DATA_ALIGN(hh->hh_len);
memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);
read_unlock_bh(&hh->hh_lock);
126
skb_push(skb, hh->hh_len);
return hh->hh_output(skb)?2:0;
} else if (dst->neighbour)
return dst->neighbour->output(skb)?2:0;
return 2;
}
/* Flushes current RTP NAT */
void rtpnat_flush(void)
{
struct ll_entry *entry;
int cntr;
/* Set NAT entries timeout to 5 secs */
nat_timeout = 5 * HZ;
/* Forbid NATing */
memset ((void *)ent_devs, 0, (ENTITIES * sizeof (struct net_device *)));
memset ((void *)set_ent_ip, 0, (ENTITIES * sizeof (unsigned int)));
ent_mask = 0x0;
/* Clear pending sessions */
while (sessions->head)
ll_remove_entry (sessions, sessions->head);
/* Return to init state */
for (cntr = DEV_ORIGIN; cntr <= DEV_TERM; cntr++)
{
for (entry = active[cntr]->head; entry; entry = entry->next)
if (((struct nat_entry *)entry->data)->active ==
NAT_ACTIVE)
*(--available[cntr]) = ((struct nat_entry *)entry-
>data)->assoc_port;
while (active[cntr]->head)
ll_remove_entry (active[cntr], active[cntr]->head);
}
/* Clear NAT table */
memset ((void *)portIDX, 0, (ENTITIES * ENT_SIZE * sizeof (struct
nat_entry)));
active_pairs = 0;
/* Clear timed-out sessions table */
127
if (tout_sessions.buffer)
kfree (tout_sessions.buffer);
if (!(tout_sessions.buffer = kmalloc (IP_RTP_TOUT_BUFFER_RESIZE *
sizeof (struct timedout_session), GFP_KERNEL)))
{
#ifdef RTPNAT_DEBUG
printk ("%s: No available memory for timed out sessions table!\n",
__FUNCTION__);
#endif
return;
}
memset (tout_sessions.buffer, 0, IP_RTP_TOUT_BUFFER_RESIZE * sizeof
(struct timedout_session));
tout_sessions.len = 0;
tout_sessions.max_len = IP_RTP_TOUT_BUFFER_RESIZE;
printk ("IP: RTP NAT flushed successfuly\n");
}
/* Return active NAT sessions */
unsigned int rtpnat_get_pairs(void)
{
return active_pairs;
}
/* RTPNAT aquire timed-out calls buffer and return 1024 entries of it */
unsigned int rtpnat_get_tout_buffer (struct timedout_buffer_usr *tbuf)
{
unsigned int len;
/* Check for expired NAT sessions and if expired - update them */
check_expired();
/* Nothing within the buffer */
if (!tout_sessions.len)
return 1;
/* Just reset the given userspace buffer */
memset (tbuf, 0, sizeof (struct timedout_buffer_usr));
if (tout_sessions.max_len <= (IP_RTP_TOUT_BUFFER_RESIZE << 1))
len = tout_sessions.len;
else
len = IP_RTP_TOUT_BUFFER_RESIZE << 1;
128
memcpy (tbuf -> buffer, tout_sessions.buffer, len);
tbuf -> len = len;
if (len == (IP_RTP_TOUT_BUFFER_RESIZE << 1))
{
struct timedout_session *tmpbuf;
if (!(tmpbuf = kmalloc (tout_sessions.len * sizeof (struct
timedout_session), GFP_KERNEL)))
{
#ifdef RTPNAT_DEBUG
printk ("%s: No available memory for timed out sessions
table temporary buffer!\n", __FUNCTION__);
#endif
return 2;
}
memcpy (tmpbuf, tout_sessions.buffer +
(IP_RTP_TOUT_BUFFER_RESIZE << 1),
tout_sessions.len - (IP_RTP_TOUT_BUFFER_RESIZE <<
1));
kfree (tout_sessions.buffer);
tout_sessions.buffer = tmpbuf;
tout_sessions.len -= (IP_RTP_TOUT_BUFFER_RESIZE << 1);
tout_sessions.max_len -= (IP_RTP_TOUT_BUFFER_RESIZE << 1);
}
else
{
/* Clear timed-out sessions table */
kfree (tout_sessions.buffer);
if (!(tout_sessions.buffer = kmalloc
(IP_RTP_TOUT_BUFFER_RESIZE * sizeof (struct timedout_session),
GFP_KERNEL)))
{
#ifdef RTPNAT_DEBUG
printk ("%s: No available memory for timed out sessions
table!\n", __FUNCTION__);
#endif
return 2;
}
memset (tout_sessions.buffer, 0, IP_RTP_TOUT_BUFFER_RESIZE
* sizeof (struct timedout_session));
tout_sessions.len = 0;
129
tout_sessions.max_len = IP_RTP_TOUT_BUFFER_RESIZE;
}
return 0;
}
/* RTPNAT deny NAT entry because of SIP BYE received */
unsigned int rtpnat_deny_pair (unsigned int src_ip, unsigned short dst_port)
{
struct nat_entry *nat;
/* Find NAT entry */
nat = &portIDX[DEV_ORIGIN][dst_port];
if (nat -> source_ip != src_ip)
nat = &portIDX[DEV_TERM][dst_port];
/* There is no such entry */
if (nat -> source_ip != src_ip)
return 1;
/* NAT entry is inactive, so do not update it */
if (nat -> active == NAT_INACTIVE || (jiffies - nat -> last) >
NAT_TIMEOUT)
return 2;
#ifdef RTPNAT_DEBUG
printk ("%s: RTPNAT - denying pair SRC_IP(%u):DST_PORT(%u)\n",
__FUNCTION__, src_ip, dst_port);
#endif
/* FORBID NAting */
memset (nat, 0, sizeof (struct nat_entry));
return 0;
}
/* NAT entries expiration set */
void rtpnat_set_expiration (unsigned long exp_time)
{
/* Set it to exp_time seconds */
nat_timeout = exp_time * HZ;
}
/* RTPNAT init function */
void __init rtpnat_init(void)
{
130
unsigned int cntr;
#ifdef RTPNAT_DEBUG
printk ("Initializing RTP NAT module\n");
#endif
/* Set NAT entries timeout to 5 secs */
nat_timeout = 5 * HZ;
/* Init sessions */
sessions = &sessions_list;
sessions->head = sessions->tail = NULL;
/* Init active ports */
for (cntr = DEV_ORIGIN; cntr <= DEV_TERM; cntr++)
{
active[cntr] = &active_list[cntr];
active[cntr]->head = active[cntr]->tail = NULL;
}
/* Allocation of available ports table */
for (cntr = DEV_ORIGIN; cntr <= DEV_TERM; cntr++)
#ifdef RTPNAT_DEBUG
{
#endif
available[cntr] = kmalloc (ENT_SIZE * sizeof (unsigned short),
GFP_KERNEL);
#ifdef RTPNAT_DEBUG
if (!available[cntr])
{
printk ("%s: No available memory for available ports table
%u!\n", __FUNCTION__, cntr);
return;
}
}
#endif
/* Init available ports */
for (cntr = 0; cntr <= PORT_RANGE; cntr ++)
available[DEV_ORIGIN][cntr] = available[DEV_TERM][cntr] =
htons(MAX_PORT - cntr);
for (cntr = (PORT_RANGE + 1); cntr <= MAX_PORT; cntr ++)
available[DEV_ORIGIN][cntr] = available[DEV_TERM][cntr] =
NAT_INACTIVE;
/* Init NAT entries */
131
memset ((void *)portIDX, 0, (ENTITIES * ENT_SIZE * sizeof (struct
nat_entry)));
/* Init Entity mask */
ent_mask = 0x0;
/* Init Entities' IPs */
memset ((void *)set_ent_ip, 0, (ENTITIES * sizeof (unsigned int)));
/* Entities netdevs init */
memset ((void *)ent_devs, 0, (ENTITIES * sizeof (struct net_device *)));
active_pairs = 0;
/* Timed-out session entries buffer + counters init */
if (!(tout_sessions.buffer = kmalloc (IP_RTP_TOUT_BUFFER_RESIZE *
sizeof (struct timedout_session), GFP_KERNEL)))
{
#ifdef RTPNAT_DEBUG
printk ("%s: No available memory for timed out sessions table!\n",
__FUNCTION__);
#endif
return;
}
memset (tout_sessions.buffer, 0, IP_RTP_TOUT_BUFFER_RESIZE * sizeof
(struct timedout_session));
tout_sessions.len = 0;
tout_sessions.max_len = IP_RTP_TOUT_BUFFER_RESIZE;
#ifdef RTPNAT_DEBUG
printk ("Initialization of RTP NAT module finished\n");
#else
printk ("IP: RTP NAT Initialized\n");
#endif
}
132
7. include/linux/ rtpnat.h
#ifndef __RTPNAT_H_
#define __RTPNAT_H_
/* Define this for debugging purposes */
#define RTPNAT_DEBUG 1
//#undef RTPNAT_DEBUG
/* Define this to see how much packets are processed by RTPNAT on stress tests */
//#define RTPNAT_STATS 1
#undef RTPNAT_STATS
/*
* Packets with characteristic !(port >> EMASK) will not be NAT-ed.
* 10 - defines exception port range from 0 to 1023
*/
#define EXCEPTION_PORTBITS 10
#define EXCEPTION_PORT(P) (!((P) >> EXCEPTION_PORTBITS))
/* Available port range */
#define MAX_PORT 0xffff
#define MIN_PORT (1 << EXCEPTION_PORTBITS)
#define PORT_RANGE (MAX_PORT - MIN_PORT)
/* Entity per-port table size */
#define ENT_SIZE (MAX_PORT + 1)
/* IP logical devices (entities) */
#define DEV_ORIGIN 0
#define DEV_TERM 1
#define ENTITIES 2
/* NAT etnries should be active or inactive */
#define NAT_INACTIVE 0
#define NAT_ACTIVE 1
/* ERRONEOUS/FORBIDDEN PORT */
#define ERRONEOUS_PORT 0
/* IP sockopt code */
#define IP_RTP_NAT_SET_DATA 0x80
/* sockopt comands */
#define IP_RTP_NAT_FLUSH 0x01
#define IP_RTP_NAT_ACTIVE_PAIRS 0x02
133
#define IP_RTP_NAT_SET_TOUT (1 << 31)
/* Timed-out session buffer enlargement constant */
#define IP_RTP_TOUT_BUFFER_RESIZE 512
/* Pre NAT table entry */
struct pre_nat_entry
{
unsigned int source_ip; /* Source IP address of the incoming
packet */
unsigned short source_port; /* Source port */
unsigned short assoc_port; /* Assoc NAT port */
unsigned int dest_ip; /* Destination IP address of the incoming
packet */
};
/* Session queue element */
struct session_entry
{
char session_id[256]; /* Session ID, given by RTPNAT daemon
*/
char session_len; /* Session ID length */
struct pre_nat_entry nat; /* NAT entry */
};
/* Deny pair structure */
struct deny_pair
{
unsigned int padding; /* Assure alignment stuff */
unsigned int src_ip;
unsigned short dst_port;
};
/* Timed-out entires */
struct timedout_session
{
unsigned int ip; /* IP of the nat etnry */
unsigned short port; /* PORT of the NAT entry */
unsigned long expired; /* When entry is detected as expired */
};
/* Timed-out buffer - USERSPACE*/
struct timedout_buffer_usr
{
struct timedout_session buffer[IP_RTP_TOUT_BUFFER_RESIZE << 1];
unsigned int len;
134
};
#ifdef __KERNEL__
/* RTP NAT table entry */
struct nat_entry
{
unsigned int source_ip; /* Source IP address of the incoming
packet */
unsigned short source_port; /* Source port */
unsigned short assoc_port; /* Associated NAT port */
unsigned short link; /* Link to the other end of the pair (by
port number as an index) */
unsigned int active; /* NAT entry is active. */
unsigned long last; /* Last time, when this entry was used (in system
ticks / jiffies) */
};
/* Double linked-list node structure */
struct ll_entry
{
void *data; /* Linked list data */
struct ll_entry *prev; /* LL previous element */
struct ll_entry *next; /* LL next element */
};
/* Double linked-list list structure */
struct ll_list
{
struct ll_entry *head, *tail; /* LL head and tail */
};
/* Timed-out buffer */
struct timedout_buffer
{
struct timedout_session *buffer;
unsigned int len;
unsigned int max_len;
};
extern unsigned int rtpnat_set_ent (unsigned int ent_ip1, unsigned int ent_ip2);
extern unsigned short rtpnat_session_registrar (struct session_entry *sentry);
extern void rtpnat_flush(void);
extern unsigned int rtpnat_get_pairs(void);
extern unsigned int rtpnat_get_tout_buffer (struct timedout_buffer_usr *tbuf);
extern unsigned int rtpnat_deny_pair (unsigned int src_ip, unsigned short dst_port);
135
extern void rtpnat_set_expiration (unsigned long exp_time);
#endif /* __KERNEL__ */
#endif /* __RTPNAT_H_ */
136
8. Примерна програма за управление от страна на userland
#include <stdio.h>
#include <linux/rtpnat.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#define INTERNAL "10.100.0.1"
#define EXTERNAL "172.16.0.1"
#define COMMAND_PORT 2048
#define MAX_DGRAM 128
#define VERSION "20040107"
#define DEBUG
int command_fd = -1, sk = -1;
int create_command_socket (void)
{
int s = -1;
struct sockaddr_in sock;
sock.sin_port = htons(COMMAND_PORT);
sock.sin_addr.s_addr = inet_addr(INTERNAL);
if (-1 != (s = socket(AF_INET, SOCK_DGRAM, 0)))
{
if(bind (s, (struct sockaddr *)&sock, sizeof(sock)))
{
fprintf(stderr, "Command socket bind: %s\n", strerror (errno));
close(s);
s = -1;
}
}
else
fprintf(stderr, "Command socket: %s\n", strerror (errno));
return s;
}
int parse_request (char *request, char *response)
{
137
int params, len;
char *token, *ptr = request;
char *tokens[20];
struct session_entry session;
params = 0;
while ((token = strsep (&ptr, " ")) && params < sizeof (tokens))
tokens[params++] = token;
if (!strcmp (tokens[1], "V"))
sprintf (response, "%s %s", tokens[0], VERSION);
else if (strstr (tokens[1], "IE") || strstr (tokens[1], "EI"))
{
char *ext_ip = (strstr (tokens[1], "IE"))?INTERNAL:EXTERNAL;
#ifdef DEBUG
struct in_addr tmp1, tmp2;
#endif
sprintf(response, "%s %d %s", tokens[0], 10000, ext_ip);
return(0);
session.session_len = strlen (tokens[2]);
strncpy (session.session_id, tokens[2], session.session_len);
inet_pton (AF_INET, tokens[3], &session.nat.source_ip);
session.nat.source_ip = ntohl(session.nat.source_ip);
session.nat.source_port = atoi (tokens[4]);
inet_pton (AF_INET, ext_ip, &session.nat.dest_ip);
session.nat.dest_ip = ntohl(session.nat.dest_ip);
len = sizeof (session);
if (-1 == getsockopt (sk, 0, IP_RTP_NAT_SET_DATA, (void *)&session,
&len))
{
fprintf (stderr, "Sending response error: %s\n", strerror (errno));
return -1;
}
sprintf (response, "%s %d %s", tokens[0], session.nat.assoc_port, ext_ip);
#ifdef DEBUG
tmp1.s_addr = htonl(session.nat.source_ip);
tmp2.s_addr = htonl(session.nat.dest_ip);
printf ("Response on request %s:%u:%s:%s is <%s>\n",
inet_ntoa (tmp1), session.nat.source_port,
inet_ntoa (tmp2), session.session_id, response);
#endif
138
}
else
return 1;
return 0;
}
void dispatcher (void)
{
int received, addr_size;
char buffer[MAX_DGRAM];
char snd_buffer[MAX_DGRAM];
struct sockaddr_in sender_address;
for(;;)
{
memset (buffer, 0, sizeof(buffer));
memset (snd_buffer, 0, sizeof(snd_buffer));
addr_size = sizeof (sender_address);
received = recvfrom (command_fd, buffer, sizeof (buffer)-1, 0, (struct sockaddr
*)&s
ender_address, &addr_size);
if (!parse_request (buffer, snd_buffer))
sendto (command_fd, snd_buffer, strlen (snd_buffer), 0, (struct sockaddr
*)&
sender_address, addr_size);
else
fprintf(stderr, "DISPATCHER: Parse error!\n");
}
}
Int main (void)
{
unsigned int ips[2], cmd;
int len;
inet_pton (AF_INET, EXTERNAL, &ips[0]);
inet_pton (AF_INET, INTERNAL, &ips[1]);
ips[0] = ntohl(ips[0]);
ips[1] = ntohl(ips[1]);
if (-1 == (sk = socket (PF_INET, SOCK_STREAM, 0)))
{
fprintf (stderr, "Couldn't open kernel communcation socket\n");
139
exit (1);
}
len = sizeof (int);
cmd = IP_RTP_NAT_FLUSH;
if (-1 == getsockopt (sk, 0, IP_RTP_NAT_SET_DATA, (void *)&cmd, &len))
fprintf (stderr, "Error %s\n", strerror (errno));
len = 2 * sizeof (int);
if (-1 == getsockopt (sk, 0, IP_RTP_NAT_SET_DATA, (void *)ips, &len))
fprintf (stderr, "Error %s\n", strerror (errno));
if (-1 == (command_fd = create_command_socket()))
exit (1);
dispatcher();
return 1;
}
140
Related docs
Other docs by pengtt
Introduction to IPv6 IPv6 deployment IPv6 Forum IPv6 Transition support IPv6 IPv4 and
Views: 5 | Downloads: 0
Get documents about "