import of upstream version 1.17
[mw/micromonitor-lm32.git] / umon_main / target / common / arp.c
1 /* arpstuff.c:
2  *      This code supports some basic arp/rarp stuff.
3  *
4  *      General notice:
5  *      This code is part of a boot-monitor package developed as a generic base
6  *      platform for embedded system designs.  As such, it is likely to be
7  *      distributed to various projects beyond the control of the original
8  *      author.  Please notify the author of any enhancements made or bugs found
9  *      so that all may benefit from the changes.  In addition, notification back
10  *      to the author will allow the new user to pick up changes that may have
11  *      been made by other users after this version of the code was distributed.
12  *
13  *      Note1: the majority of this code was edited with 4-space tabs.
14  *      Note2: as more and more contributions are accepted, the term "author"
15  *                 is becoming a mis-representation of credit.
16  *
17  *      Original author:        Ed Sutter
18  *      Email:                          esutter@lucent.com
19  *      Phone:                          908-582-2351
20  */
21 #include "config.h"
22 #if INCLUDE_ETHERNET
23 #include "endian.h"
24 #include "genlib.h"
25 #include "ether.h"
26 #include "stddefs.h"
27 #include "cli.h"
28 #include "timer.h"
29
30 static void     ArpFlush(void);
31 static void ArpShow(char *);
32 static void llas(char *);
33 static int      ArpStore(uchar *,uchar *);
34 static int      IpIsOnThisNet(uchar *);
35
36 static unsigned long probeIP;
37 static char probeAbort;
38
39 /* Constants used by Dynamic IPV4 Local-Link Address setup...
40  * (see RFC 3927)
41  */
42 #define PROBE_WAIT                      1000            /* (msec) */
43 #define PROBE_NUM                       3
44 #define PROBE_MIN                       1000            /* (msec) */
45 #define PROBE_MAX                       2000            /* (msec) */
46 #define ANNOUNCE_WAIT           2000            /* (msec) */
47 #define ANNOUNCE_NUM            2
48 #define ANNOUNCE_INTERVAL       2000            /* (msec) */
49 #define MAX_CONFLICTS           10
50 #define RATE_LIMIT_INTERVAL     60000           /* (msec) */
51 #define DEFEND_INTERVAL         10000           /* (msec) */
52
53 /* arpcache[]:
54  *      Used to store the most recent set of IP-to-MAC address correlations.
55  */
56 struct arpcache {
57         uchar   ip[4];
58         uchar   ether[6];
59 } ArpCache[SIZEOFARPCACHE];
60
61 /* ArpIdx & ArpTot:
62  *      Used for keeping track of current arp cache content.
63  */
64 int ArpIdx, ArpTot;
65
66 /* ArpStore():
67  *      Called with binary ip and ethernet addresses.
68  *      It will store that set away in the cache.
69  */
70 int
71 ArpStore(uchar *ip,uchar *ether)
72 {
73         if (EtherFromCache(ip))
74                 return(0);
75         if (ArpIdx >= SIZEOFARPCACHE)
76                 ArpIdx=0;
77         memcpy((char *)ArpCache[ArpIdx].ip,(char *)ip,4);
78         memcpy((char *)ArpCache[ArpIdx].ether,(char *)ether,6);
79         ArpIdx++;
80         ArpTot++;
81         return(0);
82 }
83
84 /* EtherFromCache():
85  *      Called with a binary (4-byte) ip address.  If a match is found
86  *      in the cache, return a pointer to that ethernet address; else
87  *      return NULL.
88  */
89 uchar *
90 EtherFromCache(uchar *ip)
91 {
92         int     i, max;
93
94         if (ArpTot >= SIZEOFARPCACHE)
95                 max = SIZEOFARPCACHE;
96                 else
97                 max = ArpTot;
98
99         for(i=0;i<SIZEOFARPCACHE;i++) {
100                 if (!memcmp((char *)ArpCache[i].ip, (char *)ip,4))
101                         return(ArpCache[i].ether);
102         }
103         return(0);
104 }
105
106 void
107 ArpFlush(void)
108 {
109         int     i;
110
111         for(i=0;i<SIZEOFARPCACHE;i++) {
112                 memset((char *)ArpCache[i].ip,0,4);
113                 memset((char *)ArpCache[i].ether,0,6);
114         }
115         ArpIdx = ArpTot = 0;
116 }
117
118 /* ArpShow():
119  *      Dump the content of the current arp cache.
120  */
121 void
122 ArpShow(char *ip)
123 {
124         struct  arpcache *ap, *end;
125
126         if (ArpTot < SIZEOFARPCACHE)
127                 end = &ArpCache[ArpTot];
128                 else
129                 end = &ArpCache[SIZEOFARPCACHE];
130
131         for (ap=ArpCache;ap<end;ap++) {
132                 if ((!ip) || (!memcmp((char *)ip, (char *)ap->ip,4))) {
133                         printf("%02x:%02x:%02x:%02x:%02x:%02x = ",
134                             ap->ether[0], ap->ether[1], ap->ether[2], ap->ether[3],
135                             ap->ether[4], ap->ether[5]);
136                         printf("%d.%d.%d.%d\n",
137                             ap->ip[0], ap->ip[1], ap->ip[2], ap->ip[3]);
138                 }
139         }
140 }
141
142 /* SendArpResp():
143  *      Called in response to an ARP REQUEST.  The incoming ethernet
144  *      header pointer points to the memory area that contains the incoming
145  *      ethernet packet that this function is called in response to.
146  *      In other words, it is used to fill the majority of the response
147  *      packet fields.
148  */
149 int
150 SendArpResp(struct ether_header *re)
151 {
152         struct ether_header *te;
153         struct arphdr *ta, *ra;
154
155         te = (struct ether_header *) getXmitBuffer();
156         memcpy((char *)&(te->ether_shost), (char *)BinEnetAddr,6);
157         memcpy((char *)&(te->ether_dhost),(char *)&(re->ether_shost),6);
158         te->ether_type = ecs(ETHERTYPE_ARP);
159
160         ta = (struct arphdr *) (te + 1);
161         ra = (struct arphdr *) (re + 1);
162         ta->hardware = ra->hardware;
163         ta->protocol = ra->protocol;
164         ta->hlen = ra->hlen;
165         ta->plen = ra->plen;
166         ta->operation = ARP_RESPONSE;
167         memcpy((char *)ta->senderha, (char *)BinEnetAddr,6);
168         memcpy((char *)ta->senderia, (char *)ra->targetia,4);
169         memcpy((char *)ta->targetha, (char *)ra->senderha,6);
170         memcpy((char *)ta->targetia, (char *)ra->senderia,4);
171         self_ecs(ta->hardware);
172         self_ecs(ta->protocol);
173         self_ecs(ta->operation);
174         sendBuffer(ARPSIZE);
175         return(0);
176 }
177
178 /* SendArpRequest():
179  * If 'probe' is non-zero, then the request is an arp-probe.
180  * Refer to RFC3927 for more on that.
181  */
182 static int
183 SendArpRequest(uchar *ip, int probe)
184 {
185         struct ether_header *te;
186         struct arphdr *ta;
187
188         /* Populate the ethernet header: */
189         te = (struct ether_header *) getXmitBuffer();
190         memcpy((char *)&(te->ether_shost), (char *)BinEnetAddr,6);
191         memcpy((char *)&(te->ether_dhost), (char *)BroadcastAddr,6);
192         te->ether_type = ecs(ETHERTYPE_ARP);
193
194         /* Populate the arp header: */
195         ta = (struct arphdr *) (te + 1);
196         ta->hardware = ecs(1);          /* 1 for ethernet */
197         ta->protocol = ecs(ETHERTYPE_IP);
198         ta->hlen = 6;           /* Length of hdware (ethernet) address */
199         ta->plen = 4;           /* Length of protocol (ip) address */
200         ta->operation = ecs(ARP_REQUEST);
201         memcpy((char *)ta->senderha, (char *)BinEnetAddr,6);
202         if (probe) {
203                 memset((char *)ta->senderia,0,4);
204                 memset((char *)ta->targetha,0,6);
205         }
206         else {
207                 memcpy((char *)ta->senderia, (char *)BinIpAddr,4);
208                 memcpy((char *)ta->targetha, (char *)BroadcastAddr,6);
209         }
210         memcpy((char *)ta->targetia, (char *)ip,4);
211         sendBuffer(ARPSIZE);
212         return(0);
213 }
214
215 /* GetBinNetMask():
216  *  Return a subnet mask in binary form.
217  *      Since this function needs a netmask, if NETMASK is not set, then
218  *      it uses the default based on the upper 3 bits of the IP address
219  *      (refer to TCP/IP Illustrated Volume 1 Pg8 for details).
220  */
221 void
222 GetBinNetMask(uchar *binnetmask)
223 {
224         char    *nm, class;
225
226         nm = getenv("NETMASK");
227         if (!nm) {
228                 memset((char *)binnetmask,0xff,4);
229                 if ((BinIpAddr[0] & 0xe0) == 0xc0) {            /* Class C */
230                         binnetmask[3] = 0;
231                         class = 'C';
232                 }
233                 else if ((BinIpAddr[0] & 0xc0) == 0x80) {       /* Class B */
234                         binnetmask[3] = 0;
235                         binnetmask[2] = 0;
236                         class = 'B';
237                 }
238                 else if ((BinIpAddr[0] & 0x80) == 0x00) {       /* Class A */
239                         binnetmask[3] = 0;
240                         binnetmask[2] = 0;
241                         binnetmask[1] = 0;
242                         class = 'A';
243                 }
244                 else
245                         class = '?';
246         }
247         else
248                 IpToBin(nm,binnetmask);
249 }
250
251 /* IpIsOnThisNet():
252  *      Return 1 if the incoming ip is on this subnet; else 0.
253  *      For each bit in the netmask that is set to 1...
254  *      If the corresponding bits in the incoming IP and this board's IP
255  *      do not match, return 0; else return 1.
256  */
257 int
258 IpIsOnThisNet(uchar *ip)
259 {
260         int             i;
261         uchar   binnetmask[8];
262
263         GetBinNetMask(binnetmask);
264
265         for(i=0;i<4;i++) {
266                 if ((ip[i] & binnetmask[i]) != (BinIpAddr[i] & binnetmask[i])) {
267                         return(0);
268                 }
269         }
270         return(1);
271 }
272
273 /* Arp():
274  *      If no args, just dump the arp cache.
275  *      If arg is present, then assume it to be an ip address.  
276  *      Check the arp cache for the presence of that ip<->correlation, if
277  *      present, print it; else issue and arp request for that IP and wait
278  *      for a response.
279  */
280
281 char *ArpHelp[] = {
282         "Address resolution protocol",
283         "-[flps:v] [IP]",
284 #if INCLUDE_VERBOSEHELP
285         " -f   flush cache",
286         " -l   dynamic config using link-local arp probe",
287         " -p   proxy arp",
288         " -s{eadr}   store eadr/IP into cache",
289 #if INCLUDE_ETHERVERBOSE
290         " -v   verbose",
291 #endif
292 #endif
293         0,
294 };
295
296 int
297 Arp(int argc,char *argv[])
298 {
299         int             opt, proxyarp, llad;
300         char    binip[8], binether[8], *storeether;
301
302         llad = proxyarp = 0;
303         storeether = (char *)0;
304         while ((opt=getopt(argc,argv,"flps:v")) != -1) {
305                 switch(opt) {
306                 case 'f':       /* Flush current arp cache */
307                         ArpFlush();
308                         break;
309                 case 'l':                       /* Dynamic ip-config using link-local addressing */
310                         llad = 1;       /* (refer to RFC 3927) */
311                         break;
312                 case 's':       /* Store specified IP/MAC combination in arp cache. */
313                         storeether = optarg;
314                         break;
315                 case 'p':       /* Assume gateway will run proxy arp */
316                         proxyarp = 1;
317                         break;
318 #if INCLUDE_ETHERVERBOSE
319                 case 'v':       /* Enable verbosity for ARP. */
320                         EtherVerbose |= SHOW_ARP;
321                         break;
322 #endif
323                 default:
324                         return(CMD_PARAM_ERROR);
325                 }
326         }
327
328         /* If llad flag is set, then we do a dynamic configuration using the
329          * link-local protocol as described in RFC 3927...
330          */
331         if (llad) {
332                 char    buf[32];
333                 struct  elapsed_tmr tmr;
334                 int             i, retry, conflicts, psval;
335
336                 dhcpDisable();
337                 probeAbort = retry = conflicts = 0;
338
339                 /* Use MAC address to establish a uniformly selected pseudo random
340                  * value between 0 and 1000...
341                  */
342                 psval = (BinEnetAddr[5] * 4);
343
344                 monDelay(psval);
345
346                 if (argc == (optind+1))
347                         IpToBin((char *)argv[optind],(unsigned char *)&probeIP);
348                 else
349                         llas((char *)&probeIP);
350
351 nextllas:
352                 SendArpRequest((uchar *)&probeIP,1);
353                 startElapsedTimer(&tmr,ANNOUNCE_WAIT);
354                 while(!msecElapsed(&tmr)) {     
355                         if (probeAbort) {
356                                 llas((char *)&probeIP);
357                                 probeAbort = retry = 0;
358                                 monDelay(psval + PROBE_MIN);
359                                 goto nextllas;
360                         }
361                         if (EtherFromCache((uchar *)&probeIP)) {
362                                 if (++conflicts > MAX_CONFLICTS)
363                                         monDelay(RATE_LIMIT_INTERVAL);
364                                 else
365                                         monDelay(psval + PROBE_MIN);
366                                 if (++retry == PROBE_NUM) {
367                                         llas((char *)&probeIP);
368                                         retry = 0;
369                                 }
370                                 goto nextllas;
371                         }
372                         pollethernet();
373                 }
374                 /* If we're here, then we found an IP address that is not being
375                  * used on the local subnet...
376                  */
377                 setenv("IPADD",IpToString(probeIP,buf));
378                 setenv("NETMASK","255.255.0.0");
379                 setenv("GIPADD",0);
380                 EthernetStartup(EtherVerbose,0);
381                 for(i=0;i<ANNOUNCE_NUM;i++) {
382                         SendArpRequest(BinIpAddr,0);
383                         monDelay(ANNOUNCE_INTERVAL);
384                 }
385         }
386         else if (argc == (optind+1)) {
387                 IpToBin((char *)argv[optind],(unsigned char *)binip);
388                 if (storeether) {
389                         EtherToBin((char *)storeether, (unsigned char *)binether);
390                         ArpStore((unsigned char *)binip,(unsigned char *)binether);
391                 }
392                 else {
393                         if (ArpEther((unsigned char *)binip,(unsigned char *)0,proxyarp))
394                                 ArpShow(binip);
395                 }
396         }
397         else
398                 ArpShow(0);
399 #if INCLUDE_ETHERVERBOSE
400         EtherVerbose &= ~ SHOW_ARP;
401 #endif
402         return(CMD_SUCCESS);
403 }
404
405 /* ArpEther():
406  *      Retrieve the ethernet address that corresponds to the incoming IP 
407  *      address.  First check the local ArpCache[], then if not found issue
408  *      an ARP request... If the incoming IP is on this net, then issue the
409  *      request for the MAC address of that IP; otherwise, issue the request
410  *      for this net's GATEWAY.
411  */
412 uchar *
413 ArpEther(uchar *binip, uchar *ecpy, int proxyarp)
414 {
415         char    *gip;
416         struct  elapsed_tmr tmr;
417         uchar   gbinip[8], *Ip, *ep;
418         int             timeoutsecs, retry;
419
420         if (!EtherIsActive) {
421                 printf("Ethernet disabled\n");
422                 return(0);
423         }
424
425         /* First check local cache. If found, return with pointer to MAC. */
426         ep = EtherFromCache(binip);
427         if (ep) {
428                 if (ecpy) {
429                         memcpy((char *)ecpy, (char *)ep,6);
430                         ep = ecpy;
431                 }
432                 return(ep);
433         }
434
435         retry = 0;
436         RetransmitDelay(DELAY_INIT_ARP);
437         while(1) {
438                 /* If IP is not on this net, then get the GATEWAY IP address. */
439                 if (!proxyarp && !IpIsOnThisNet(binip)) {
440                         gip = getenv("GIPADD");
441                         if (gip) {
442                                 IpToBin(gip,gbinip);
443                                 if (!IpIsOnThisNet(gbinip)) {
444                                         printf("GIPADD/IPADD subnet confusion.\n");
445                                         return(0);
446                                 }
447                                 ep = EtherFromCache(gbinip);
448                                 if (ep) {
449                                         if (ecpy) {
450                                                 memcpy((char *)ecpy, (char *)ep,6);
451                                                 ep = ecpy;
452                                         }
453                                         return(ep);
454                                 }
455                                 SendArpRequest(gbinip,0);
456                                 Ip = gbinip;
457                         }
458                         else {
459                                 SendArpRequest(binip,0);
460                                 Ip = binip;
461                         }
462                 }
463                 else {
464                         SendArpRequest(binip,0);
465                         Ip = binip;
466                 }
467                 if (retry) {
468                         printf("  ARP Retry #%d (%d.%d.%d.%d)\n",retry,
469                                 binip[0],binip[1],binip[2],binip[3]);
470                 }
471
472                 /* Now that the request has been issued, wait for a while to see if
473                  * the ARP cache is loaded with the result of the request.
474                  */
475                 timeoutsecs = RetransmitDelay(DELAY_OR_TIMEOUT_RETURN);
476                 if (timeoutsecs == RETRANSMISSION_TIMEOUT)
477                         break;
478                 startElapsedTimer(&tmr,timeoutsecs*1000);
479                 while(!msecElapsed(&tmr)) {
480                         ep = EtherFromCache(Ip);
481                         if (ep)
482                                 break;
483
484                         pollethernet();
485                 }
486                 if (ep) {
487                         if (ecpy) {
488                                 memcpy((char *)ecpy, (char *)ep,6);
489                                 ep = ecpy;
490                         }
491                         return(ep);
492                 }
493                 RetransmitDelay(DELAY_INCREMENT);
494                 retry++;
495         }
496 #if INCLUDE_ETHERVERBOSE
497         if ((EtherVerbose & SHOW_ARP) && (retry))
498                 printf("  ARP giving up\n");
499 #endif
500         return(0);
501 }
502
503 /* processRARP();
504  *      Called by the fundamental ethernet driver code to process a RARP
505  *      request.
506  */
507 int
508 processRARP(struct ether_header *ehdr,ushort size)
509 {
510         struct  arphdr *arpp;
511
512         arpp = (struct arphdr *)(ehdr+1);
513         self_ecs(arpp->hardware);
514         self_ecs(arpp->protocol);
515         self_ecs(arpp->operation);
516
517         switch(arpp->operation) {
518         case RARP_RESPONSE:
519                 if (!memcmp((char *)arpp->targetha, (char *)BinEnetAddr,6)) {
520 #if INCLUDE_ETHERVERBOSE
521                         if (EtherVerbose & SHOW_ARP) {
522                                 printf("  RARP Response from %d.%d.%d.%d\n",
523                                     arpp->senderia[0],arpp->senderia[1],
524                                     arpp->senderia[2],arpp->senderia[3]);
525                                 printf("  MY IP: from %d.%d.%d.%d\n",
526                                     arpp->targetia[0],arpp->targetia[1],
527                                     arpp->targetia[2],arpp->targetia[3]);
528                         }
529 #endif
530                         memcpy((char *)BinIpAddr, (char *)arpp->targetia,6);
531                 }
532                 break;
533         case RARP_REQUEST:
534                 break;
535         default:
536                 printf("  Invalid RARP operation: 0x%x\n",arpp->operation);
537                 return(-1);
538         }
539         return(0);
540 }
541
542 /* processARP();
543  *      Called by the fundamental ethernet driver code to process a ARP
544  *      request.
545  */
546 char *
547 processARP(struct ether_header *ehdr,ushort size)
548 {
549         struct  arphdr *arpp;
550
551         arpp = (struct arphdr *)(ehdr+1);
552         self_ecs(arpp->hardware);
553         self_ecs(arpp->protocol);
554         self_ecs(arpp->operation);
555
556         /* If the sender IP address is all zeroes, then assume this is
557          * an arp probe.  If we are currently doing a probe, and the
558          * address we are probing matches the target IP address of this
559          * probe, then set a flag that will abort the current probe.
560          */
561         if ((arpp->senderia[0] == 0) && (arpp->senderia[1] == 0) &&
562                 (arpp->senderia[2] == 0) && (arpp->senderia[3] == 0)) {
563                         if (memcmp((char *)arpp->targetia,(char *)&probeIP,4) == 0)
564                                 probeAbort = 1;
565         }
566
567         switch(arpp->operation) {
568         case ARP_REQUEST:
569                 if (!memcmp((char *)arpp->targetia, (char *)BinIpAddr,4)) {
570                         ArpStore(arpp->senderia,arpp->senderha);
571                         SendArpResp(ehdr);
572                 }
573                 break;
574         case ARP_RESPONSE:
575                 if (!memcmp((char *)arpp->targetia, (char *)BinIpAddr,4)) {
576                         if (!memcmp((char *)arpp->targetia,(char *)arpp->senderia,4))
577                                 printf("WARNING: IP %s may be in use on network\n",IPadd);
578
579                         ArpStore(arpp->senderia,arpp->senderha);
580                 }
581                 break;
582         default:
583                 printf("  Invalid ARP operation: 0x%x\n",arpp->operation);
584                 printPkt(ehdr,(int)size,ETHER_INCOMING);
585                 return((char *)0);
586         }
587         return((char *)(arpp + 1));
588 }
589
590 /* sendGratuitousARP():
591  * As defined in RFC 3220...
592  *
593  *  An ARP packet sent by a node in order to spontaneously
594  *  cause other nodes to update an entry in their ARP cache.
595  *
596  * Issue a "gratuitous ARP" (RFC 3220) for two reasons...
597  * - make sure no other host is already using this IP address.
598  * - cause other devices on cable to update their arp cache.
599  */
600 void
601 sendGratuitousArp(void)
602 {
603         SendArpRequest(BinIpAddr,0);
604 }
605
606 /* llas():
607  * Link local address selection.
608  * Referring to sec 2.1 of RFC3927, select an address using a pseudo-random
609  * number generator to assign a number in the range from
610  * 169.254.1.0 (0xa9fe0100) thru 169.254.254.255 (0xa9fefeff).
611  * To do this, we run a 32-bit CRC on the 6-byte MAC address, then add the
612  * least significant 8 bits of that value to the base of the address
613  * range.  Each successive time this function is called, then increment
614  * by the least significant 4 bits of the LSB of the MAC. 
615  */
616 #define LLAD_BEGIN      0xa9fe0100
617 #define LLAD_END        0xa9fefeff
618
619 static void
620 llas(char *ipptr)
621 {
622         static char beenhere;
623         static unsigned long llad;
624         unsigned long pseudorandval, tmp;
625
626         if (beenhere == 0) {
627                 pseudorandval = crc32(BinEnetAddr,6);
628                 llad = LLAD_BEGIN + (pseudorandval & 0xff); 
629         }
630         else {
631                 pseudorandval = (unsigned long)(BinEnetAddr[5] & 0xf);
632                 llad += pseudorandval;
633                 if (llad >= LLAD_END)
634                         llad = LLAD_BEGIN + pseudorandval + beenhere;
635         }
636         beenhere++;
637
638         printf("LLAD: %d.%d.%d.%d\n",
639                 (llad & 0xff000000) >> 24, (llad & 0xff0000) >> 16,
640                 (llad & 0xff00) >> 8, (llad & 0xff));
641
642         tmp = ecl(llad);
643
644         if (ipptr)
645                 memcpy(ipptr,(char *)&tmp,4);
646 }
647
648 #endif