added sparse test
[tridge/junkcode.git] / atalk_bridge.c
1 /* simple appletalk bridge for rodney
2
3    Andrew Tridgell 1997
4 */
5
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <math.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <limits.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <netdb.h>
16 #include <errno.h>
17 #include <endian.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/ioctl.h>
21 #include <sys/time.h>
22 #include <net/if.h>
23 #include <netinet/in.h>
24 #include <netinet/ip.h>
25 #include <netinet/udp.h>
26 #include <arpa/nameser.h>
27 #include <arpa/inet.h>
28 #include <linux/if_ether.h>
29
30 #define DEBUG 0
31
32 #define MAX_PACKET_LENGTH 1600 
33 #define HISTORY_LENGTH 20
34
35 /* we should use /etc/ethers but I'm too lazy */
36 #if 0
37 #define HUB    "00:00:0C:13:6F:18"
38 #else
39 #define HUB    "00:00:C0:81:93:8E"
40 #endif
41 #define ETH0   "00:00:C0:1B:A2:B8"
42 #define ETH1   "00:00:C0:6A:71:99"
43
44 static int eth_fd0 = -1;
45 static int eth_fd1 = -1;
46
47 static char *if_eth0 = "eth0";  /* name of ethernet interface */
48 static char *if_eth1 = "eth1";  /* name of ethernet interface */
49
50 static char data[MAX_PACKET_LENGTH]; 
51
52 static unsigned char hub_addr[ETH_ALEN];
53 static unsigned char eth0_addr[ETH_ALEN];
54 static unsigned char eth1_addr[ETH_ALEN];
55
56 static unsigned history[HISTORY_LENGTH];
57 static int history_pointer;
58
59 static unsigned eth0_counter, eth1_counter, dup_counter, ip_counter;
60
61 /* parse an ethernet address from string format to a byte array */
62 static void parse_ether(unsigned char *addr, char *addr_str)
63 {
64         unsigned x[6];
65         if (sscanf(addr_str, "%x:%x:%x:%x:%x:%x",
66                    &x[0], &x[1], &x[2], 
67                    &x[3], &x[4], &x[5]) != 6) {
68                 fprintf(stderr,"couldn't parse %s\n", addr_str);
69         } 
70         addr[0] = x[0]; addr[1] = x[1]; addr[2] = x[2];
71         addr[3] = x[3]; addr[4] = x[4]; addr[5] = x[5];
72 }
73
74 /* pretty-print an ethernet address */
75 static char *ether_str(unsigned char *addr)
76 {
77         static char s[2][30];
78         static int i;
79         char *ret;
80
81         ret = s[i];
82
83         sprintf(ret,"%02x:%02x:%02x:%02x:%02x:%02x",
84                 addr[0], addr[1], addr[2], 
85                 addr[3], addr[4], addr[5]);
86
87         i = (i+1)%2;
88
89         return ret;
90 }
91
92
93 /* fetch the protocol field in a ethernet or 802 packet */
94 static unsigned get_protocol(char *data)
95 {
96         unsigned short proto;
97
98         /* its either at offset 12 or 20 */
99         proto = *(unsigned short *)(data + 12);
100         proto = ntohs(proto);
101
102         if (proto < 1500) {
103                 /* it must be an 802 packet */
104                 proto = *(unsigned short *)(data + 20);
105                 proto = ntohs(proto);           
106         }
107         
108         return proto;
109 }
110
111 /* a really simple checksum routine, probably quite hopeless */
112 static unsigned csum(char *data,int len)
113 {
114         unsigned sum=0;
115         int i;
116         for (i=0;i<len;i++)
117                 sum ^= data[i] << ((i*15) % 28);
118         return sum;
119 }
120
121
122 /* close the ethernet fd and set it back to non-promiscuous */
123 static void eth_close(int fd, char *interface)
124 {
125         struct ifreq ifr;
126
127         strcpy(ifr.ifr_name, interface);
128         if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
129                 perror("SIOCGIFFLAGS");
130                 return;
131         }
132
133         ifr.ifr_flags &= ~IFF_PROMISC;
134         if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
135                 perror("SIOCSIFFLAGS");
136                 return;
137         }
138
139         close(fd);
140 }
141
142
143
144 /* open an ethernet socket for the requested protocol and
145    return a file descriptor that can be used for reading/writing packets
146    to that interface
147  
148    pstr specifies what packet types to accept
149  */
150 static int eth_open(char *interface, char *pstr)
151 {
152         int fd;
153         int protocol = -1;
154         struct ifreq ifr;
155
156         if (!strcmp(pstr,"802.2")) protocol=ETH_P_802_2;
157         if (!strcmp(pstr,"802.3")) protocol=ETH_P_802_3;
158         if (!strcmp(pstr,"etherII")) protocol=ETH_P_IPX;
159         if (!strcmp(pstr,"echo")) protocol=ETH_P_ECHO;
160         if (!strcmp(pstr,"x25")) protocol=ETH_P_X25;
161         if (!strcmp(pstr,"arp")) protocol=ETH_P_ARP;
162         if (!strcmp(pstr,"atalk")) protocol=ETH_P_ATALK;
163         if (!strcmp(pstr,"aarp")) protocol=ETH_P_AARP;
164         if (!strcmp(pstr,"all")) protocol=ETH_P_ALL;
165
166         if (protocol == -1) {
167                 printf("Unknown protocol [%s]\n",pstr);
168                 return -1;
169         }
170
171         if ((fd = socket(AF_INET, SOCK_PACKET, htons(protocol)))<0) {
172                 perror("Error opening local ethernet socket\n");
173                 return -1;
174         }
175
176         strcpy(ifr.ifr_name, interface);
177         if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
178                 perror("SIOCGIFFLAGS");
179                 return -1;
180         }
181
182         ifr.ifr_flags |= IFF_PROMISC;
183         if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
184                 perror("SIOCSIFFLAGS");
185                 return -1;
186         }
187
188         return fd;
189 }
190
191
192 static int eth_write(int fd, char *data, int len,char *iface)
193 {
194         struct sockaddr dst;
195
196         dst.sa_family = AF_INET;
197         strcpy(dst.sa_data,iface);
198
199         return sendto(fd,data,len,0,&dst,sizeof(dst));
200 }
201
202 static int eth_read(int fd, char *buf)
203 {
204         struct sockaddr_in from;
205         size_t fromlen = sizeof(from);
206
207         return recvfrom(fd, buf, MAX_PACKET_LENGTH, 0,
208                         (struct sockaddr *) &from, &fromlen);
209 }
210
211 static void Usage(void)
212 {
213         printf("Usage: \natalk_bridge\n\n");
214         printf("atalk_bridge [-f fr] [-0 eth0] [-1 eth1] port\n\n");
215         printf("fr = Ethernet frame type to bridge. Default is 802.2.\n");
216         printf("     Known values are:\n");
217         printf("         802.2 802.3 etherII echo x25 arp atalk aarp all\n");
218         printf("if = Network interface to read from (default is eth0)\n");
219 }
220
221
222 void sig_int(int sig)
223 {
224         eth_close(eth_fd0, if_eth0);
225         eth_close(eth_fd1, if_eth1);
226         printf("Wrote %d packets to eth0\n", eth0_counter);
227         printf("Wrote %d packets to eth1\n", eth1_counter);
228         printf("%d duplicate packets dropped\n", dup_counter);
229         printf("%d IP packets processed\n", ip_counter);
230         exit(0);
231 }
232
233
234 /* this is the guts of the program. A packet has arrived and we need to decide
235    what to do with it */
236 static void process_packet(char *data, int len)
237 {
238         int i;
239         unsigned sum;
240         struct ethhdr *hdr;
241         unsigned protocol;
242
243         if (len <= 0) return;
244
245         sum = csum(data, len);
246
247         /* work out where it is from */
248         hdr = (struct ethhdr *)data;
249         protocol = get_protocol(data);
250
251 #if DEBUG
252         printf("packet %s -> %s len=%4d proto=%04x sum=%08x  ",
253                ether_str(hdr->h_source), 
254                ether_str(hdr->h_dest), 
255                len, protocol, sum);
256 #endif
257
258         /* if the packet is from or to us then it makes no sense
259            to bridge it */
260         if (memcmp(hdr->h_source, eth0_addr, ETH_ALEN) == 0 ||
261             memcmp(hdr->h_source, eth1_addr, ETH_ALEN) == 0 ||
262             memcmp(hdr->h_dest, eth0_addr, ETH_ALEN) == 0 ||
263             memcmp(hdr->h_dest, eth1_addr, ETH_ALEN) == 0) {
264                 /* its from us, drop it */
265 #if DEBUG
266                 printf("own packet\n");
267 #endif
268                 return;
269         }
270
271         /* don't process any ARP or RARP packets */
272         if (protocol == ETH_P_ARP || protocol == ETH_P_RARP) {
273 #if DEBUG
274                 printf("ARP\n");
275 #endif
276                 return;
277         }
278
279         /* initially process IP packets to and from the hub, at
280            least until the bridge gets established. Once the arp tables
281            get flushed we will just be able to drop all these packets */
282         if (protocol == ETH_P_IP &&
283             memcmp(hdr->h_source, hub_addr, ETH_ALEN) == 0 &&
284             memcmp(hdr->h_dest,   hub_addr, ETH_ALEN) == 0) {
285 #if DEBUG
286                 printf("IP\n");
287 #endif
288                 return;
289         }
290
291         /* is it a duplicate? */
292         for (i=0;i<HISTORY_LENGTH;i++)
293                 if (sum == history[i]) {
294 #if DEBUG
295                         printf("duplicate %d\n", i);
296 #endif
297                         history[i] = 0;
298                         dup_counter++;
299                         return;
300                 }
301         history[history_pointer] = sum;
302         history_pointer = (history_pointer+1) % HISTORY_LENGTH;
303
304         if (protocol == ETH_P_IP) {
305                 printf("packet %s -> %s len=%4d proto=%04x sum=%08x\n",
306                        ether_str(hdr->h_source), 
307                        ether_str(hdr->h_dest), 
308                        len, protocol, sum);
309                 ip_counter++;
310         }
311                         
312         if (memcmp(hdr->h_source, hub_addr, ETH_ALEN) == 0) {
313                 /* its from the hub (presumed to be on eth1). Send it to
314                    eth0 */
315 #if DEBUG
316                 printf("to eth0\n");
317 #endif
318                 printf("packet %s -> %s len=%4d proto=%04x sum=%08x\n",
319                        ether_str(hdr->h_source), 
320                        ether_str(hdr->h_dest), 
321                        len, protocol, sum);
322                 eth0_counter++;
323                 eth_write(eth_fd0, data, len, if_eth0);
324         } else {
325                 /* its from a host on eth1, pass it on to eth1 */
326 #if DEBUG
327                 printf("to eth1\n");
328 #endif
329                 eth1_counter++;
330                 eth_write(eth_fd1, data, len, if_eth1);
331         }
332 }
333
334
335 int main(int argc, char *argv[])
336 {
337         int i;
338         char *pstr = "all";
339
340         if(getuid()) {
341                 fprintf(stderr, "must be superuser\n");
342                 exit(1);
343         }
344
345         fprintf(stderr,"atalk_bridge by Andrew Tridgell\n");
346
347         /* Process command-line args */
348         for(i=1; i<argc && argv[i][0]=='-'; ++i) switch(argv[i][1])
349                 {
350                 case 'f':
351                    /* Selected frame types - from linux/if_ether.h */
352                    pstr = argv[++i];
353                    break;
354                 case '0': if_eth0 = argv[++i]; break;
355                 case '1': if_eth1 = argv[++i]; break;
356                 default:
357                         Usage();
358                         break;
359                 }
360
361         parse_ether(hub_addr, HUB);
362         parse_ether(eth0_addr, ETH0);
363         parse_ether(eth1_addr, ETH1);
364
365         eth_fd0 = eth_open(if_eth0, pstr);
366         if (eth_fd0 == -1) {
367                 fprintf(stderr,"can't open %s\n", if_eth0);
368                 exit(1);
369         }
370
371         eth_fd1 = eth_open(if_eth1, pstr);
372         if (eth_fd1 == -1) {
373                 fprintf(stderr,"can't open %s\n", if_eth1);
374                 eth_close(eth_fd0, if_eth0);
375                 exit(1);
376         }
377
378         signal(SIGINT, sig_int);
379
380         printf("bridge started\n");
381
382         while (1) {
383                 fd_set  rfd;
384                 int len, no;
385                 struct timeval tv;
386
387                 FD_ZERO(&rfd);
388                 FD_SET(eth_fd0, &rfd);
389                 FD_SET(eth_fd1, &rfd);
390
391                 tv.tv_sec = 2;
392                 tv.tv_usec = 0;
393
394                 no = select(32, &rfd, NULL, NULL, &tv);
395
396                 if (no == -1 && errno != EAGAIN) {
397                         printf("select error\n");
398                         break;
399                 }
400
401                 if (no == 0) {
402                         /* zero the history */
403                         memset(history, 0, sizeof(history));
404                         continue;
405                 }
406
407                 /* is anything happening on the ethernet? */
408                 if (FD_ISSET(eth_fd0, &rfd)) {
409                         len = eth_read(eth_fd0, data);
410                         process_packet(data, len);
411                 }
412
413 #if 0
414                 /* this is commented out as it seems that we get all
415                    packets on both file sockets! */
416                 if (FD_ISSET(eth_fd1, &rfd)) {
417                         len = eth_read(eth_fd1, data);
418                         process_packet(data, len);
419                 }
420 #endif
421         }
422
423         return 0;
424 }