import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / Documentation / networking / ethertap.txt
1 NOTE: Ethertap is now an obsolete facility, and is scheduled
2       to be removed in the 2.5.x kernel series.  Those writing
3       applications using ethertap should convert their code to
4       use the TUN/TAP driver instead, see 'tuntap.txt' in this
5       directory for more details.  -DaveM
6
7 Ethertap programming mini-HOWTO
8 -------------------------------
9
10 The ethertap driver was written by Jay Schulist <jschlst@samba.org>,
11 you should contact him for further information. This document was written by
12 bert hubert <bert.hubert@netherlabs.nl>. Updates are welcome.
13
14 What ethertap can do for you
15 ----------------------------
16
17 Ethertap allows you to easily run your own network stack from userspace.
18 Tunnels can benefit greatly from this. You can also use it to do network
19 experiments. The alternative would be to use a raw socket to send data and
20 use libpcap to receive it. Using ethertap saves you this multiplicity and
21 also does ARP for you if you want.
22
23 The more technical blurb:
24
25 Ethertap provides packet reception and transmission for user space programs.
26 It can be viewed as a simple Ethernet device, which instead of receiving
27 packets from a network wire, it receives them from user space.
28
29 Ethertap can be used for anything from AppleTalk to IPX to even building
30 bridging tunnels. It also has many other general purpose uses.
31
32 Configuring your kernel
33 -----------------------
34
35 Firstly, you need this in Networking Options:
36
37         #
38         # Code maturity level options
39         #
40         CONFIG_EXPERIMENTAL=y
41
42 Then you need Netlink support:
43
44         CONFIG_NETLINK=y
45
46 This allows the kernel to exchange data with userspace applications. There
47 are two ways of doing this, the new way works with netlink sockets and I
48 have no experience with that yet. ANK uses it in his excellent iproute2
49 package, see for example rtmon.c. iproute2 can be found on
50 ftp://ftp.inr.ac.ru/ip-routing/iproute2*
51
52 The new way is described, partly in netlink(7), available on 
53 http://www.europe.redhat.com/documentation/man-pages/man7/netlink.7.php3
54
55 There is also a Netlink-HOWTO, available on http://snafu.freedom.org/linux2.2/docs/netlink-HOWTO.html
56 Sadly I know of no code using ethertap with this new interface.
57
58 The older way works by opening character special files with major node 36.
59 Enable this with:
60
61         CONFIG_NETLINK_DEV=m
62
63 Please be advised that this support is going to be dropped somewhere in the
64 future!
65
66 Then finally in the Network Devices section, 
67
68         CONFIG_ETHERTAP=m
69
70 You can include it directly in the kernel if you want, of course, no need
71 for modules.
72
73 Setting it all up
74 -----------------
75
76 First we need to create the /dev/tap0 device node:
77
78         # mknod /dev/tap0 c 36 16
79         # mknod /dev/tap1 c 36 17
80         (etc)
81
82 Include the relevant modules (ethertap.o, netlink_dev.o, perhaps netlink.o),
83 and bring up your tap0 device:
84
85         # ifconfig tap0 10.0.0.123 up
86
87 Now your device is up and running, you can ping it as well. This is what
88 confused me to no end, because nothing is connected to our ethertap as yet,
89 how is it that we can ping it?
90
91 It turns out that the ethertap is just like a regular network interface -
92 even when it's down you can ping it. We need to route stuff to it:
93
94         # route add -host 10.0.0.124 gw 10.0.0.123
95
96 Now we can read /dev/tap0 and when we ping 10.0.0.124 from our
97 localhost, output should appear on the screen.
98
99         # cat /dev/tap0
100         :ßVU:9````````````````````````þýþET@?'
101
102
103 Getting this to work from other hosts
104 -------------------------------------
105
106 For this to work, you often need proxy ARP. 
107
108         # echo 1 > /proc/sys/net/ipv4/conf/eth0/proxy_arp 
109
110 eth0 here stands for the interface that connects to 'other hosts'.
111
112 Chances are that you are trying this on a non-routing desktop computer, so
113 you need to enable ip forwarding:
114
115         # echo 1 > /proc/sys/net/ipv4/ip_forward 
116
117 You should now be able to ping 10.0.0.124 from other hosts on your
118 10.0.0.0/8 subnet. If you are using public ip space, it should work from
119 everywhere.
120
121 ARP
122 ---
123
124 If we were to take things very literally, your tcp/ip pseudo stack would
125 also have to implement ARP and MAC addresses. This is often a bit silly as
126 the ethertap device is a figment of our imagination anyway. However, should
127 you want to go 'all the way', you can add the 'arp' flag to ifconfig:
128
129         # ifconfig tap0 10.0.0.123 up arp
130
131 This may also be useful when implementing a bridge, which needs to bridge
132 ARP packets as well.
133
134 The sample program below will no longer work then, because it does not
135 implement ARP. 
136
137 Sample program
138 --------------
139
140 A sample program is included somewhere in the bowels of the netfilter
141 source. I've extracted this program and list it here. It implements a very
142 tiny part of the IP stack and can respond to any pings it receives. It gets
143 confused if it receives ARP, as it tries to parse it by treating it as an IP
144 packet.
145
146 /* Simple program to listen to /dev/tap0 and reply to pings. */
147 #include <fcntl.h>
148 #include <netinet/ip.h>
149 #include <netinet/ip_icmp.h>
150 #if defined(__GLIBC__) && (__GLIBC__ == 2)
151 #include <netinet/tcp.h>
152 #include <netinet/udp.h>
153 #else
154 #include <linux/tcp.h>
155 #include <linux/udp.h>
156 #endif
157 #include <string.h>
158 #include <stdio.h>
159 #include <errno.h>
160 #include <unistd.h>
161
162 u_int16_t csum_partial(void *buffer, unsigned int len, u_int16_t prevsum)
163 {
164         u_int32_t sum = 0;
165         u_int16_t *ptr = buffer;
166
167         while (len > 1)  {
168                 sum += *ptr++;
169                 len -= 2;
170         }
171         if (len) {
172                 union {
173                         u_int8_t byte;
174                         u_int16_t wyde;
175                 } odd;
176                 odd.wyde = 0;
177                 odd.byte = *((u_int8_t *)ptr);
178                 sum += odd.wyde;
179         }
180         sum = (sum >> 16) + (sum & 0xFFFF);
181         sum += prevsum;
182         return (sum + (sum >> 16));
183 }
184
185 int main()
186 {
187         int fd, len;
188         union {
189                 struct {
190                         char etherhdr[16];
191                         struct iphdr ip;
192                 } fmt;
193                 unsigned char raw[65536];
194         } u;
195
196         fd = open("/dev/tap0", O_RDWR);
197         if (fd < 0) {
198                 perror("Opening `/dev/tap0'");
199                 return 1;
200         }
201
202         /* u.fmt.ip.ihl in host order!  Film at 11. */
203         while ((len = read(fd, &u, sizeof(u))) > 0) {
204                 u_int32_t tmp;
205                 struct icmphdr *icmp
206                         = (void *)((u_int32_t *)&u.fmt.ip + u.fmt.ip.ihl );
207                 struct tcphdr *tcp = (void *)icmp;
208                 struct udphdr *udp = (void *)icmp;
209                 
210                 fprintf(stderr, "SRC = %u.%u.%u.%u DST = %u.%u.%u.%u\n",
211                         (ntohl(u.fmt.ip.saddr) >> 24) & 0xFF,
212                         (ntohl(u.fmt.ip.saddr) >> 16) & 0xFF,
213                         (ntohl(u.fmt.ip.saddr) >> 8) & 0xFF,
214                         (ntohl(u.fmt.ip.saddr) >> 0) & 0xFF,
215                         (ntohl(u.fmt.ip.daddr) >> 24) & 0xFF,
216                         (ntohl(u.fmt.ip.daddr) >> 16) & 0xFF,
217                         (ntohl(u.fmt.ip.daddr) >> 8) & 0xFF,
218                         (ntohl(u.fmt.ip.daddr) >> 0) & 0xFF);
219
220                 switch (u.fmt.ip.protocol) {
221                 case IPPROTO_ICMP:
222                         if (icmp->type == ICMP_ECHO) {
223                                 fprintf(stderr, "PONG! (iphdr = %u bytes)\n",
224                                         (unsigned int)((char *)icmp
225                                                        - (char *)&u.fmt.ip));
226
227                                 /* Turn it around */
228                                 tmp = u.fmt.ip.saddr;
229                                 u.fmt.ip.saddr = u.fmt.ip.daddr;
230                                 u.fmt.ip.daddr = tmp;
231
232                                 icmp->type = ICMP_ECHOREPLY;
233                                 icmp->checksum = 0;
234                                 icmp->checksum
235                                         = ~csum_partial(icmp,
236                                                         ntohs(u.fmt.ip.tot_len)
237                                                         - u.fmt.ip.ihl*4, 0);
238
239                                 {
240                                         unsigned int i;
241                                         for (i = 44;
242                                              i < ntohs(u.fmt.ip.tot_len); i++){
243                                                 printf("%u:0x%02X ", i,
244                                                        ((unsigned char *)
245                                                         &u.fmt.ip)[i]);
246                                         }
247                                         printf("\n");
248                                 }
249                                 write(fd, &u, len);
250                         }
251                         break;
252                 case IPPROTO_TCP:
253                         fprintf(stderr, "TCP: %u -> %u\n", ntohs(tcp->source),
254                                 ntohs(tcp->dest));
255                         break;
256
257                 case IPPROTO_UDP:
258                         fprintf(stderr, "UDP: %u -> %u\n", ntohs(udp->source),
259                                 ntohs(udp->dest));
260                         break;
261                 }
262         }
263         if (len < 0)
264                 perror("Reading from `/dev/tap0'");
265         else fprintf(stderr, "Empty read from `/dev/tap0'");
266         return len < 0 ? 1 : 0;
267 }
268