Страниц: [1]
  Печать  
Автор Тема: алиасные IP адреса  (Прочитано 4231 раз)
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« : Сентября 15, 2003, 01:11:00 am »

Для любого  сетевого интерфейса вы можете, помимо основного IP адреса, конфигурировать несколько (сколько?) алиасных IP адресов, т.е. интерфейс, скажем /dev/io-net/en0 может иметь N IP адресов - 1 основной и N-1 алиасных. Сделать это можно конфигуратором сети Photon, или, если кому это больше нравится - ручной правкой /etc/net.cfg (не забудьте после такой правки выполнить команду netmanager). Сделаем - смотрим (ifconfig -a):

/root # ifconfig -a
lo0: flags=8009<UP,LOOPBACK,MULTICAST> mtu 33220
       inet 127.0.0.1 netmask 0xff000000
       inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
       inet6 ::1 prefixlen 128
en0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
       address: 00:4f:49:00:b0:f9
       inet 10.0.0.35 netmask 0xffffffe0 broadcast 10.0.0.63
       inet alias 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255
       inet alias 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255
       inet alias 192.168.0.3 netmask 0xffffff00 broadcast 192.168.0.255
       inet6 fe80::24f:49ff:fe00:b0f9%en0 prefixlen 64 scopeid 0x2

- например так.
Обратите внимание - IP интерфейса и его алиасные IP принадлежат разным подсеткам (см. маски) - это само по себе может быть нетривиально использовано в приложениях, например: получение запроса из подсетки "1" и ответ в подсетку "2", но оставим это пока...

Напишем UDP приложение ("приложение № 1"), которое запускает pthread_create() по числу IP алиасов (т.е.за-bind-овывает все алиасные IP)... Это очень простой и простейший вариант "многоканального" UDP клиента, когда сервер отвечает всегда по IP адресу запросившего клиента,и обработка в нём может быть асинхронная - это совсем не тривиальная задача (аналог многопоточного TCP клиента-сервера, но там соответствие поддерживается однозначностью канала, созданного connect()).

А вот теперь сделаем простейшее приложение ("приложение №2"), которое по UDP, bind-уясь с основного IP адреса (то, что пишут во всех иллюстрационных учебниках ), в котором есть где-то такой фрагмент кода:

static addr local, remote;
... // создаём сокет sockfd...
local.sin_port = htons( LOCAL_PORT );
inet_aton( "localhost", &local.sin_addr );
remote.sin_port = htons( SERVER_PORT );
if( inet_aton( argv[ argc - 1 ], &remote.sin_addr ) == 0 ) perror( "..." );
if( bind( sockfd, (struct sockaddr *)&local, SIZE ) == -1 ) {
  close( sockfd );
  perror( "..." );
};
// ... а дальше (здесь - не важно, это - цикл приёма - передачи):
while( true ) {
  sendto( sockfd, ... );
  struct timeval tv = { READTIMEOUT, 0 };
  fd_set rfd; FD_ZERO( &rfd ); FD_SET( sockfd, &rfd );
  if( select( sockfd + 1, &rfd, NULL, NULL, &tv ) > 0 ) {
     RepMsg Rez;
     socklen_t fromlen = sizeof( struct sockaddr_in );
     if( ( n = recvfrom( sockfd, ... ) > 0 ) ) {
     }
     else perror(...);
  }
  else perror(...);
};

Это - выкроенный фрагмент из большего реального проект, прошу не обращать внимания на детали, меня сейчас интересует только inet_aton( ..., &local.sin_addr ) и следующий за ним bind( sockfd, ... ).

1. запускаем "приложение №2" - всё Ок, всё работает... (если не работает - то я просто неправильно выкроил фрагмент из этого приложения ).

2. запускаем "приложение №1", после чего пытаемся запустить "приложение №2"... Вот тот bind( sockfd, ... ) - вызывает отшибку. Т.е., что произошло? Мы за-bind-или все алиасные IP, а при попытке bind на "localhost" - получаем ошибку.

3. поменяем одну строчку кода (перед bind):
inet_aton( "10.0.0.35", &local.sin_addr );
- т.е. явно при-bind-ить сокет с локальной стороны на основной IP - всё Ок, оба приложения работают совместно...

4. ещё вариант - тут мне один "умник" подсказал... это в качестве анекдота:
inet_aton( "127.0.0.1", &local.sin_addr );
- кстати, работает очень "интересно" - сокет биндится и пакеты отсылаются серверу... но ответов от него нет - естественно, сервер понимает 127.0.0.1 совсем по-другому, и отправляет... сам себе, в /dev/null .

Теперь вопросы:

1. я понимаю, что ни у кого не возникало столь специфической ситуации, но может кто-то может высказать предположения, чем принципиально отличаются bind на localhost и 10.0.0.35 (я предполагаю, что по localhost - bind-уется по 0.0.0.0, ну и что из того?...), и почему bind на localhost "перекрывается" с bind-ами по алиасным IP интерфейса?

2. как простейшим способом получить реальный IP интерфейса, который есть localhost (не используя /etc/hosts & DNS)?


[ Это Сообщение было отредактировано: Olej в 2003-09-14 22:15 ]
Записан
Landy
Jr. Member
**
Offline Offline

Сообщений: 65


Просмотр профиля WWW
« Ответ #1 : Сентября 15, 2003, 11:27:00 am »

 Может это и не в тему, у меня была такая ситуация(и сейчас есть)
Пакеты UDP отсылались на клиента по его адресу(пусть 10.0.0.1), а клиент читал их из 127.0.0.1.
Это нормальное поведение? Может это натолкнет на мысли

PS. QNX NC 6.2.1
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #2 : Сентября 16, 2003, 07:04:00 pm »

Кое-что проясняется, может это кому будет полезно?:
1. к вопросу - как установить реальный IP своего интерфейса, чтоб к нему bind-овать локальный конец сокета...

1-й способ:

#include <sys/utsname.h>
struct utsname sysinfo;
if( uname( &sysinfo ) == -1 )  {
perror( "uname" );  return EXIT_FAILURE;
};
cout << "UNAME: " << sysinfo.nodename << endl;


2-й способ:
[code]
#include <unistd.h>
#include <sys/param.h>
char sname[ MAXHOSTNAMELEN + 1 ]; //определено в <sys/param.h>
if( gethostname( sname, sizeof( sname ) ) == -1 )
perror( "uname" );  return EXIT_FAILURE;
};
cout << "GETHOSTNAME: " << sysinfo.nodename << endl;
[code]

Получаем что-то типа:
/WORK/SCod/scod/src/gcc_ntox86 # start
UNAME: new
GETHOSTNAME: new
- new - с таким именем у меня конфигурировался в net.cfg хост, всё ОК, обращаю внимание, это есть у У.Стивенса - POSIX не регламентирует в каком виде возвращается имя хоста, в кратком, или полном доменном, разные UNIX делают это по разному.

Теперь к коду выше (который я приводил) - я там сам "нахомутал" нет проверки кода возврата inet_aton()... поставим везде:

// до всякого присвоение local.sin_addr:
cout << "before - " << inet_ntoa( local.sin_addr ) << endl;
....
if( inet_aton( "127.0.0.1", &local.sin_addr ) == 0 )
  perror( "invalid address string" );  
cout << "127.0.0.1 - " << inet_ntoa( local.sin_addr ) << endl;
if( inet_aton( "localhost", &local.sin_addr ) == 0 )
  perror( "invalid address string" );  
cout << "localhost - " << inet_ntoa( local.sin_addr ) << endl;    
if( inet_aton( "18.0.0.200", &local.sin_addr ) == 0 )
  perror( "invalid address string" );      
cout << "18.0.0.200 - " << inet_ntoa( local.sin_addr ) << endl;    
....
if( inet_aton( sysinfo.nodename, &local.sin_addr ) == 0 )
  perror( "invalid address string" );  
cout << "new - " << inet_ntoa( local.sin_addr ) << endl;

Чтоб не мудрить, и не заставлять вас читать этот текст внимательно, объясню "на падьцах" - в локальный адрес сокета я заношу поочерёдно (я сразу за значением показываю вывод программы):

- до всякого присвоения (before) - 0.0.0.0 - это INADDR_ANY;
- 127.0.0.1 - 127.0.0.1;
- localhost - invalid address string;
- 18.0.0.200 - 18.0.0.200;
- new - invalid address string;

Всё понятно: все функции преобразования адресов из "символьной" формы, работают только с символьной записью в десятичной нотации, и всё! (я как-то это забыл), и для получения даже локального адреса localhost - нужно привлекать IP-резолвер ( /etc/hosts, DNS etc.)!


#include <netdb.h>
struct hostent *phst = gethostbyname( sysinfo.nodename );
if( phst == NULL ) cout << "unknown host!" << endl;
else {
  for( int i = 0; phst->h_addr_list[ i ] != 0; i++ )
  cout << inet_ntoa( *(struct in_addr*)phst->h_addr_list[ i ] ) << endl;  
  local.sin_addr = *(struct in_addr*)phst->h_addr_list[ 0 ];
};  

Я специально поставил полный цикл вывода всех известных IP хоста (помните, что у меня на интерфейсе много алиасных IP)... и получаю в выводе ... 1-но значение 18.0.0.200! Т.е. мы получим, в общем случае, для хоста список IP, но - список IP его интерфейсов, но никак не алиасных IP, присвоенных интерфейсам.

Кстати, интересно заменить строчку (имени хоста) на:
struct hostent *phst = gethostbyname( "localhost" );
- естественно, вы увидите разрешение в 127.0.0.1.

Чтоб всё это увидеть, естественно нужно прописать localhost (он там после установки и так прописан) и new (имя вашего хоста) в /etc/hosts. В противном случае - вы увидите unknown host! - даже для тривиального localhost.

После bind к полученному реальному IP, сокет не конфликтует с сокетами, открытыми на алиасных IP...

Мне кажется, что описанные эффекты имеют прямое касательство с проблемами получения UDP-широковещания, обсуждаемыми параллельно в соседней теме...
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #3 : Сентября 16, 2003, 07:05:00 pm »

Кое-что проясняется, может это кому будет полезно?:
1. к вопросу - как установить реальный IP своего интерфейса, чтоб к нему bind-овать локальный конец сокета...

1-й способ:

#include <sys/utsname.h>
struct utsname sysinfo;
if( uname( &sysinfo ) == -1 )  {
perror( "uname" );  return EXIT_FAILURE;
};
cout << "UNAME: " << sysinfo.nodename << endl;


2-й способ:

#include <unistd.h>
#include <sys/param.h>
char sname[ MAXHOSTNAMELEN + 1 ]; //определено в <sys/param.h>
if( gethostname( sname, sizeof( sname ) ) == -1 )
perror( "uname" );  return EXIT_FAILURE;
};
cout << "GETHOSTNAME: " << sysinfo.nodename << endl;


Получаем что-то типа:
/WORK/SCod/scod/src/gcc_ntox86 # start
UNAME: new
GETHOSTNAME: new
- new - с таким именем у меня конфигурировался в net.cfg хост, всё ОК, обращаю внимание, это есть у У.Стивенса - POSIX не регламентирует в каком виде возвращается имя хоста, в кратком, или полном доменном, разные UNIX делают это по разному.

Теперь к коду выше (который я приводил) - я там сам "нахомутал" нет проверки кода возврата inet_aton()... поставим везде:

// до всякого присвоение local.sin_addr:
cout << "before - " << inet_ntoa( local.sin_addr ) << endl;
....
if( inet_aton( "127.0.0.1", &local.sin_addr ) == 0 )
  perror( "invalid address string" );  
cout << "127.0.0.1 - " << inet_ntoa( local.sin_addr ) << endl;
if( inet_aton( "localhost", &local.sin_addr ) == 0 )
  perror( "invalid address string" );  
cout << "localhost - " << inet_ntoa( local.sin_addr ) << endl;    
if( inet_aton( "18.0.0.200", &local.sin_addr ) == 0 )
  perror( "invalid address string" );      
cout << "18.0.0.200 - " << inet_ntoa( local.sin_addr ) << endl;    
....
if( inet_aton( sysinfo.nodename, &local.sin_addr ) == 0 )
  perror( "invalid address string" );  
cout << "new - " << inet_ntoa( local.sin_addr ) << endl;

Чтоб не мудрить, и не заставлять вас читать этот текст внимательно, объясню "на падьцах" - в локальный адрес сокета я заношу поочерёдно (я сразу за значением показываю вывод программы):

- до всякого присвоения (before) - 0.0.0.0 - это INADDR_ANY;
- 127.0.0.1 - 127.0.0.1;
- localhost - invalid address string;
- 18.0.0.200 - 18.0.0.200;
- new - invalid address string;

Всё понятно: все функции преобразования адресов из "символьной" формы, работают только с символьной записью в десятичной нотации, и всё! (я как-то это забыл), и для получения даже локального адреса localhost - нужно привлекать IP-резолвер ( /etc/hosts, DNS etc.)!


#include <netdb.h>
struct hostent *phst = gethostbyname( sysinfo.nodename );
if( phst == NULL ) cout << "unknown host!" << endl;
else {
  for( int i = 0; phst->h_addr_list[ i ] != 0; i++ )
  cout << inet_ntoa( *(struct in_addr*)phst->h_addr_list[ i ] ) << endl;  
  local.sin_addr = *(struct in_addr*)phst->h_addr_list[ 0 ];
};  

Я специально поставил полный цикл вывода всех известных IP хоста (помните, что у меня на интерфейсе много алиасных IP)... и получаю в выводе ... 1-но значение 18.0.0.200! Т.е. мы получим, в общем случае, для хоста список IP, но - список IP его интерфейсов, но никак не алиасных IP, присвоенных интерфейсам.

Кстати, интересно заменить строчку (имени хоста) на:
struct hostent *phst = gethostbyname( "localhost" );
- естественно, вы увидите разрешение в 127.0.0.1.

Чтоб всё это увидеть, естественно нужно прописать localhost (он там после установки и так прописан) и new (имя вашего хоста) в /etc/hosts. В противном случае - вы увидите unknown host! - даже для тривиального localhost.

После bind к полученному реальному IP, сокет не конфликтует с сокетами, открытыми на алиасных IP...

Мне кажется, что описанные эффекты имеют прямое касательство с проблемами получения UDP-широковещания, обсуждаемыми параллельно в соседней теме...


[ Это Сообщение было отредактировано: Olej в 2003-09-16 16:16 ]
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #4 : Сентября 16, 2003, 07:32:00 pm »

Для интересующихся привожу правленный код того, что было в изначальном постинге - теперь сокеты на алиасных IP не конфликтуют с основным IP:

#define SIZE sizeof( struct sockaddr_in )
class addr : public sockaddr_in {
public:  
  addr( void ) {
     memset( (void*)this, 0, SIZE );
     sin_len = SIZE; sin_family =  AF_INET;
  };
};

Сразу прошу прощения - в исходном коде встречаются определения типа addr - а их там не приведено... Вот это он и есть - это фактически sockaddr_in, но чтоб инициализации не делать каждый раз я переопределил его так... А вот дальше - исполнимый фрагмент того, что было раньше:

addr local;
local.sin_port = htons( LOCAL_PORT );
// получаем имя локального хоста
struct utsname sysinfo;
if( uname( &sysinfo ) == -1 )  Terminate( "..." );
// получаем IP (основной) локального хоста
struct hostent *phst = gethostbyname( sysinfo.nodename );
if( phst == NULL ) Terminate( "..." );
local.sin_addr = *(struct in_addr*)phst->h_addr_list[ 0 ];

... а вот теперь bind() и всё далее...

P.S. ... чуть-чуть по ширине подправил ...


[ Это Сообщение было отредактировано: Olej в 2003-09-16 17:55 ]
Записан
Страниц: [1]
  Печать  
 
Перейти в: