Media _RTP_ relaying

Document Sample
Media _RTP_ relaying Powered By Docstoc
					Подобряване производителността
   на преноса на 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

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:79
posted:6/3/2011
language:Bulgarian
pages:140