Пишу прогу для ip-over-udp туннелинга, которая использует интерфейс tun(4).
При запуске программа должна открыть файл /dev/tunN, присвоить интерфейсу локальный и удаленный адреса, поменять mtu, ну и поднять его. Все это вроде бы работает, т.е. запускаем 2 копии программы для 2 концов туннеля, получаем:
ifconfig:
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
inet 192.168.3.2 --> 192.168.3.1 netmask 0xffffff00
Opened by PID 71348
tun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
inet 192.168.3.1 --> 192.168.3.2 netmask 0xffffff00
Opened by PID 71353
netstat -rn:
Destination Gateway Flags Refs Use Netif Expire
192.168.3.1 192.168.3.2 UH 0 0 tun0
192.168.3.2 192.168.3.1 UH 0 0 tun1
Но, если сделать ping 192.168.3.1, то пакеты идут не в tun, а в rl0, т.е. к default gateway, где, ессно, грохаются.
Самое интересное: если после запуска прог и поднятия ими интерфейсов сделать так:
ifconfig tun0 inet 192.168.3.2 192.168.3.1
ifconfig tun1 inet 192.168.3.1 192.168.3.2
то есть присвоить интерфейсам те же адреса, что у них уже есть, все начинает отлично работать.
Инициализируются у меня интерфейсы так:
// Open tun device
tundev = open(fulldevname, O_RDWR);
// Name the interface
bzero(&ifra, sizeof(ifra));
bzero(&ifr, sizeof(ifr));
strcpy(ifr.ifr_name, devname);
strcpy(ifra.ifra_name, devname);
// Open control socket
int s;
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
// Set interface addresses
struct sockaddr_in *psin;
psin = (struct sockaddr_in *)&(ifra.ifra_addr);
psin->sin_len = sizeof(struct sockaddr_in);
psin->sin_family = AF_INET;
memcpy(&(psin->sin_addr), &locaddr, sizeof(struct sockaddr_in));
psin = (struct sockaddr_in *)&(ifra.ifra_broadaddr);
psin->sin_len = sizeof(struct sockaddr_in);
psin->sin_family = AF_INET;
memcpy(&(psin->sin_addr), &remaddr, sizeof(struct sockaddr_in));
ioctl(s, SIOCAIFADDR, &ifra);
// Set interface mtu
ioctl(s, SIOCGIFMTU, &ifr);
oldmtu = ifr.ifr_mtu;
ifr.ifr_mtu = mtu;
ioctl(s, SIOCSIFMTU, &ifr);
// bring the interface UP
ioctl(s, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP;
ioctl(s, SIOCSIFFLAGS, &ifr);
close(s);
Ума не приложу что я упустил, может SIOCDIFADDR надо делать?
Посмотрел исходники nos-tun (программа для IP/IP туннелинга, есть в комплекте FreeBSD), так там вот что написано:
/*
* Delete (previous) addresses for interface
*
* !!!!
* On FreeBSD this ioctl returns error
* when tunN have no addresses, so - log and ignore it.
*
*/
if (ioctl(s, SIOCDIFADDR, &ifra) < 0) {
syslog(LOG_ERR,"SIOCDIFADDR - %m");
}
Собственно, конкретные вопросы:
- почему у меня пакеты начинают ходить в интерфейс tun только когда я сделаю ему ifconfig с его собственными адресами (фактически ничего не изменив), хотя в таблице роутинга черным по белому написано что 192.168.3.1 через tun?
- если все-таки нужно делать SIODIFADDR, то как это делать правильно? Как в nos-tun, просто сделать и игнорировать возможные ошибки, или как-то более правильно можно?
Спасибо.