From owner-freebsd-stable Wed Feb 18 09:54:55 1998 Return-Path: Received: (from majordom@localhost) by hub.freebsd.org (8.8.8/8.8.8) id JAA25692 for freebsd-stable-outgoing; Wed, 18 Feb 1998 09:54:55 -0800 (PST) (envelope-from owner-freebsd-stable@FreeBSD.ORG) Received: from newserv.urc.ac.ru (newserv.urc.ac.ru [193.233.85.48]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id JAA25188; Wed, 18 Feb 1998 09:52:06 -0800 (PST) (envelope-from joy@urc.ac.ru) Received: from urc.ac.ru (y.urc.ac.ru [193.233.85.37]) by newserv.urc.ac.ru (8.8.8/8.8.8) with ESMTP id WAA24422; Wed, 18 Feb 1998 22:51:06 +0500 (ES) (envelope-from joy@urc.ac.ru) Message-ID: <34EB1F8A.A2543E68@urc.ac.ru> Date: Wed, 18 Feb 1998 22:51:06 +0500 From: Konstantin Chuguev Organization: South Ural Center of FREEnet X-Mailer: Mozilla 4.04 [en] (X11; I; FreeBSD 3.0-CURRENT i386) MIME-Version: 1.0 To: freebsd-hackers@FreeBSD.ORG, freebsd-stable@FreeBSD.ORG Subject: Some problems with raw IP programming Content-Type: text/plain; charset=koi8-r Content-Transfer-Encoding: 7bit Sender: owner-freebsd-stable@FreeBSD.ORG Precedence: bulk Hi, IP networking gurus :-) Please help me to solve these problems: I wrote a small daemon using tun* devices for IP-in-IP tunnel. (Did not like any soft I saw before :-) It works almost fine (under 2.2.5-STABLE), but has 3 problems. 1. I use connected sockets (i.e. bind() and connect()): --- struct in_addr local_ip, remote_ip ; int raw_ip, tunnel, max_fd ; struct sockaddr_in local_ipaddr, remote_ipaddr ; const int true = 1 ; /* Initialization of local_ip and remote_ip with inet_aton() stripped... */ raw_ip = socket( PF_INET, SOCK_RAW, IPPROTO_IPIP ) ; if( is_syserror( raw_ip < 0, LOG_ERR, "Can't open raw socket" ) ) return ; if( is_syserror( setsockopt( raw_ip, SOL_SOCKET, SO_REUSEADDR, & true, sizeof( true ) ), LOG_WARNING, "Can't reuse local address %s", local_addr ) ) err_printf() ; remote_ipaddr.sin_family = AF_INET ; remote_ipaddr.sin_addr = remote_ip ; if( is_syserror( connect( raw_ip, (struct sockaddr *)(& remote_ipaddr), sizeof( remote_ipaddr ) ), LOG_ERR, "Can't connect to remote address %s", remote_addr ) ) return ; local_ipaddr.sin_family = AF_INET ; local_ipaddr.sin_addr = local_ip ; local_ipaddr.sin_port = 0 ; if( is_syserror( bind( raw_ip, (struct sockaddr *)(& local_ipaddr), sizeof( local_ipaddr ) ), LOG_WARNING, "Can't bind socket to local address %s", local_addr ) ) err_printf() ; --- Running the daemon, I get: RR03:~# /usr/local/sbin/ipipd -i tun0 195.54.2.138 195.54.5.39 ipipd: Can't bind socket to local address 195.54.2.138: Can't assign requested address RR03:~# This error has number 49 - EADDRNOTAVAIL. But: RR03:~# ifconfig -a sl0: flags=9011 mtu 552 inet 195.54.2.138 --> 195.54.2.137 netmask 0xffffff00 Now I just ignore this error, and all works. Since there is only one route to the remote end of the tunnel, and it is through sl0 interface, I can be sure that IPIP packets have the source address 195.54.2.138. But if I have several possible routes (interfaces) from the local side of the tunnel, then the router at the remote end may reject packets with another source address :-( The same picture with fxp0 interface. Any suggestions? 2. When I receive IPIP (ICMP-in-IP, pinging through a tunnel) packets from the raw socket: --- size = recv( raw_ip, buffer, sizeof( buffer ), 0 ) ; if( is_syserror( size < 0, LOG_CRIT, "Can't read from %s", "raw socket" ) ) return ; h_len = (unsigned)( ((struct ip *)buffer)->ip_hl ) << 2 ; /* log_printf( LOG_INFO, "Size=%d, ip_len=%d, h_len=%d\n", size, ((struct ip *)buffer)->ip_len, h_len ) ; */ size = write( tunnel, buffer + h_len, /* ((struct ip *)buffer)->ip_len */ size - h_len ) ; --- h_len is equal to 20 (the correct size of the IP packet without any options); size (the result of recv() or read()) is equal to 104 (20 bytes for the envelope IP header, other 20 ones for the internal IP (ICMP) header, other 64 for the ICMP body); the ICMP packet INSIDE the IPIP header looks pretty good, BUT: ((struct ip *)buffer)->ip_len is 84 !!! i.e. EXACTLY 20 less then the "size" variable and always EQUAL TO the size of the ENCLOSED IP packet. So I have to use "size - hlen" for writing packets into tun*, or "((struct ip *)buffer)->ip_len", but not "((struct ip *)buffer)->ip_len - hlen" IP format description says the last should be correct, but it is not. 3. Sometimes (very seldom, can happen once a week), I get a message in syslog: Feb 11 03:50:05 Chelyabinsk-RNOC-RR03 ipipd[6835]: Can't write to raw socket: No buffer space available or Feb 18 22:13:45 Chelyabinsk-RNOC-RR03 ipipd[17393]: Can't write to raw socket: N o route to host This is from the code: --- size = write( raw_ip, buffer, size ) ; if( is_syserror( size < 0, LOG_CRIT, "Can't write to %s", "raw socket" ) ) { err_printf() ; continue ; } --- I ignore this error, preferring to lose a packet than to make a program more complicate (now it is just one endless loop with select() inside). But sometimes (even more seldom) that error causes the daemon to behave strange: Ping to the opposite side of the tunnel doesn't work. When I tcpdump the tun0 interface, I see packets in both directions, and I see the packets from the tunnel forwarded to other interfaces. When I tcpdump the sl0 interface (through which the tunnel comes), I see ONLY IPIP packets from the opposite side. When I restart the daemon, all gets OK. What can it be? Sorry for so long questions :-) Thanks. -- Konstantin V. Chuguev. System administrator of Ural Regional Center of FREEnet, Joy@urc.ac.ru Chelyabinsk, Russia. To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-stable" in the body of the message