5592dc2b9c806010234b89a6dfa760d438bcd704
[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 int      ArpStore(uchar *,uchar *);
33 static int      IpIsOnThisNet(uchar *);
34
35 /* arpcache[]:
36  *      Used to store the most recent set of IP-to-MAC address correlations.
37  */
38 struct arpcache {
39         uchar   ip[4];
40         uchar   ether[6];
41 } ArpCache[SIZEOFARPCACHE];
42
43 /* ArpIdx & ArpTot:
44  *      Used for keeping track of current arp cache content.
45  */
46 int ArpIdx, ArpTot;
47
48 /* ArpStore():
49  *      Called with binary ip and ethernet addresses.
50  *      It will store that set away in the cache.
51  */
52 int
53 ArpStore(uchar *ip,uchar *ether)
54 {
55         if (EtherFromCache(ip))
56                 return(0);
57         if (ArpIdx >= SIZEOFARPCACHE)
58                 ArpIdx=0;
59         memcpy((char *)ArpCache[ArpIdx].ip,(char *)ip,4);
60         memcpy((char *)ArpCache[ArpIdx].ether,(char *)ether,6);
61         ArpIdx++;
62         ArpTot++;
63         return(0);
64 }
65
66 /* EtherFromCache():
67  *      Called with a binary (4-byte) ip address.  If a match is found
68  *      in the cache, return a pointer to that ethernet address; else
69  *      return NULL.
70  */
71 uchar *
72 EtherFromCache(uchar *ip)
73 {
74         int     i, max;
75
76         if (ArpTot >= SIZEOFARPCACHE)
77                 max = SIZEOFARPCACHE;
78                 else
79                 max = ArpTot;
80
81         for(i=0;i<SIZEOFARPCACHE;i++) {
82                 if (!memcmp((char *)ArpCache[i].ip, (char *)ip,4))
83                         return(ArpCache[i].ether);
84         }
85         return(0);
86 }
87
88 void
89 ArpFlush(void)
90 {
91         int     i;
92
93         for(i=0;i<SIZEOFARPCACHE;i++) {
94                 memset((char *)ArpCache[i].ip,0,4);
95                 memset((char *)ArpCache[i].ether,0,6);
96         }
97         ArpIdx = ArpTot = 0;
98 }
99
100 /* ArpShow():
101  *      Dump the content of the current arp cache.
102  */
103 void
104 ArpShow(char *ip)
105 {
106         struct  arpcache *ap, *end;
107
108         if (ArpTot < SIZEOFARPCACHE)
109                 end = &ArpCache[ArpTot];
110                 else
111                 end = &ArpCache[SIZEOFARPCACHE];
112
113         for (ap=ArpCache;ap<end;ap++) {
114                 if ((!ip) || (!memcmp((char *)ip, (char *)ap->ip,4))) {
115                         printf("%02x:%02x:%02x:%02x:%02x:%02x = ",
116                             ap->ether[0], ap->ether[1], ap->ether[2], ap->ether[3],
117                             ap->ether[4], ap->ether[5]);
118                         printf("%d.%d.%d.%d\n",
119                             ap->ip[0], ap->ip[1], ap->ip[2], ap->ip[3]);
120                 }
121         }
122 }
123
124 /* SendArpResp():
125  *      Called in response to an ARP REQUEST.  The incoming ethernet
126  *      header pointer points to the memory area that contains the incoming
127  *      ethernet packet that this function is called in response to.
128  *      In other words, it is used to fill the majority of the response
129  *      packet fields.
130  */
131 int
132 SendArpResp(struct ether_header *re)
133 {
134         struct ether_header *te;
135         struct arphdr *ta, *ra;
136
137         te = (struct ether_header *) getXmitBuffer();
138         memcpy((char *)&(te->ether_shost), (char *)BinEnetAddr,6);
139         memcpy((char *)&(te->ether_dhost),(char *)&(re->ether_shost),6);
140         te->ether_type = ecs(ETHERTYPE_ARP);
141
142         ta = (struct arphdr *) (te + 1);
143         ra = (struct arphdr *) (re + 1);
144         ta->hardware = ra->hardware;
145         ta->protocol = ra->protocol;
146         ta->hlen = ra->hlen;
147         ta->plen = ra->plen;
148         ta->operation = ARP_RESPONSE;
149         memcpy((char *)ta->senderha, (char *)BinEnetAddr,6);
150         memcpy((char *)ta->senderia, (char *)ra->targetia,4);
151         memcpy((char *)ta->targetha, (char *)ra->senderha,6);
152         memcpy((char *)ta->targetia, (char *)ra->senderia,4);
153         self_ecs(ta->hardware);
154         self_ecs(ta->protocol);
155         self_ecs(ta->operation);
156         sendBuffer(ARPSIZE);
157         return(0);
158 }
159
160 /* SendArpRequest():
161  */
162 int
163 SendArpRequest(uchar *ip)
164 {
165         struct ether_header *te;
166         struct arphdr *ta;
167
168         /* Populate the ethernet header: */
169         te = (struct ether_header *) getXmitBuffer();
170         memcpy((char *)&(te->ether_shost), (char *)BinEnetAddr,6);
171         memcpy((char *)&(te->ether_dhost), (char *)BroadcastAddr,6);
172         te->ether_type = ecs(ETHERTYPE_ARP);
173
174         /* Populate the arp header: */
175         ta = (struct arphdr *) (te + 1);
176         ta->hardware = ecs(1);          /* 1 for ethernet */
177         ta->protocol = ecs(ETHERTYPE_IP);
178         ta->hlen = 6;           /* Length of hdware (ethernet) address */
179         ta->plen = 4;           /* Length of protocol (ip) address */
180         ta->operation = ecs(ARP_REQUEST);
181         memcpy((char *)ta->senderha, (char *)BinEnetAddr,6);
182         memcpy((char *)ta->senderia, (char *)BinIpAddr,4);
183         memcpy((char *)ta->targetha, (char *)BroadcastAddr,6);
184         memcpy((char *)ta->targetia, (char *)ip,4);
185         sendBuffer(ARPSIZE);
186         return(0);
187 }
188
189 /* GetBinNetMask():
190  *  Return a subnet mask in binary form.
191  *      Since this function needs a netmask, if NETMASK is not set, then
192  *      it uses the default based on the upper 3 bits of the IP address
193  *      (refer to TCP/IP Illustrated Volume 1 Pg8 for details).
194  */
195 void
196 GetBinNetMask(uchar *binnetmask)
197 {
198         char    *nm, class;
199
200         nm = getenv("NETMASK");
201         if (!nm) {
202                 memset((char *)binnetmask,0xff,4);
203                 if ((BinIpAddr[0] & 0xe0) == 0xc0) {            /* Class C */
204                         binnetmask[3] = 0;
205                         class = 'C';
206                 }
207                 else if ((BinIpAddr[0] & 0xc0) == 0x80) {       /* Class B */
208                         binnetmask[3] = 0;
209                         binnetmask[2] = 0;
210                         class = 'B';
211                 }
212                 else if ((BinIpAddr[0] & 0x80) == 0x00) {       /* Class A */
213                         binnetmask[3] = 0;
214                         binnetmask[2] = 0;
215                         binnetmask[1] = 0;
216                         class = 'A';
217                 }
218                 else
219                         class = '?';
220         }
221         else
222                 IpToBin(nm,binnetmask);
223 }
224
225 /* IpIsOnThisNet():
226  *      Return 1 if the incoming ip is on this subnet; else 0.
227  *      For each bit in the netmask that is set to 1...
228  *      If the corresponding bits in the incoming IP and this board's IP
229  *      do not match, return 0; else return 1.
230  */
231 int
232 IpIsOnThisNet(uchar *ip)
233 {
234         int             i;
235         uchar   binnetmask[8];
236
237         GetBinNetMask(binnetmask);
238
239         for(i=0;i<4;i++) {
240                 if ((ip[i] & binnetmask[i]) != (BinIpAddr[i] & binnetmask[i])) {
241                         return(0);
242                 }
243         }
244         return(1);
245 }
246
247 /* Arp():
248  *      If no args, just dump the arp cache.
249  *      If arg is present, then assume it to be an ip address.  
250  *      Check the arp cache for the presence of that ip<->correlation, if
251  *      present, print it; else issue and arp request for that IP and wait
252  *      for a response.
253  */
254
255 char *ArpHelp[] = {
256         "Address resolution protocol",
257         "-[fps:v] [IP]",
258 #if INCLUDE_VERBOSEHELP
259         " -f   flush cache",
260         " -p   proxy arp",
261         " -s{eadr}   store eadr/IP into cache",
262 #if INCLUDE_ETHERVERBOSE
263         " -v   verbose",
264 #endif
265 #endif
266         0,
267 };
268
269 int
270 Arp(int argc,char *argv[])
271 {
272         int             opt, proxyarp;
273         char    binip[8], binether[8], *storeether;
274
275         proxyarp = 0;
276         storeether = (char *)0;
277         while ((opt=getopt(argc,argv,"fps:v")) != -1) {
278                 switch(opt) {
279                 case 'f':       /* Flush current arp cache */
280                         ArpFlush();
281                         break;
282                 case 's':       /* Store specified IP/MAC combination in arp cache. */
283                         storeether = optarg;
284                         break;
285                 case 'p':       /* Assume gateway will run proxy arp */
286                         proxyarp = 1;
287                         break;
288 #if INCLUDE_ETHERVERBOSE
289                 case 'v':       /* Enable verbosity for ARP. */
290                         EtherVerbose |= SHOW_ARP;
291                         break;
292 #endif
293                 default:
294                         return(CMD_PARAM_ERROR);
295                 }
296         }
297
298         if (argc == (optind+1)) {
299                 IpToBin((char *)argv[optind],(unsigned char *)binip);
300                 if (storeether) {
301                         EtherToBin((char *)storeether, (unsigned char *)binether);
302                         ArpStore((unsigned char *)binip,(unsigned char *)binether);
303                 }
304                 else {
305                         if (ArpEther((unsigned char *)binip,(unsigned char *)0,proxyarp))
306                                 ArpShow(binip);
307                 }
308         }
309         else
310                 ArpShow(0);
311 #if INCLUDE_ETHERVERBOSE
312         EtherVerbose &= ~ SHOW_ARP;
313 #endif
314         return(CMD_SUCCESS);
315 }
316
317 /* ArpEther():
318  *      Retrieve the ethernet address that corresponds to the incoming IP 
319  *      address.  First check the local ArpCache[], then if not found issue
320  *      an ARP request... If the incoming IP is on this net, then issue the
321  *      request for the MAC address of that IP; otherwise, issue the request
322  *      for this net's GATEWAY.
323  */
324 uchar *
325 ArpEther(uchar *binip, uchar *ecpy, int proxyarp)
326 {
327         char    *gip;
328         struct  elapsed_tmr tmr;
329         uchar   gbinip[8], *Ip, *ep;
330         int             timeoutsecs, retry;
331
332         if (!EtherIsActive) {
333                 printf("Ethernet disabled\n");
334                 return(0);
335         }
336
337         /* First check local cache. If found, return with pointer to MAC. */
338         ep = EtherFromCache(binip);
339         if (ep) {
340                 if (ecpy) {
341                         memcpy((char *)ecpy, (char *)ep,6);
342                         ep = ecpy;
343                 }
344                 return(ep);
345         }
346
347         retry = 0;
348         RetransmitDelay(DELAY_INIT_ARP);
349         while(1) {
350                 /* If IP is not on this net, the get the GATEWAY IP address. */
351                 if (!proxyarp && !IpIsOnThisNet(binip)) {
352                         gip = getenv("GIPADD");
353                         if (gip) {
354                                 IpToBin(gip,gbinip);
355                                 if (!IpIsOnThisNet(gbinip)) {
356                                         printf("GIPADD/IPADD subnet confusion.\n");
357                                         return(0);
358                                 }
359                                 ep = EtherFromCache(gbinip);
360                                 if (ep) {
361                                         if (ecpy) {
362                                                 memcpy((char *)ecpy, (char *)ep,6);
363                                                 ep = ecpy;
364                                         }
365                                         return(ep);
366                                 }
367                                 SendArpRequest(gbinip);
368                                 Ip = gbinip;
369                         }
370                         else {
371                                 SendArpRequest(binip);
372                                 Ip = binip;
373                         }
374                 }
375                 else {
376                         SendArpRequest(binip);
377                         Ip = binip;
378                 }
379                 if (retry) {
380                         printf("  ARP Retry #%d (%d.%d.%d.%d)\n",retry,
381                                 binip[0],binip[1],binip[2],binip[3]);
382                 }
383
384                 /* Now that the request has been issued, wait for a while to see if
385                  * the ARP cache is loaded with the result of the request.
386                  */
387                 timeoutsecs = RetransmitDelay(DELAY_OR_TIMEOUT_RETURN);
388                 if (timeoutsecs == RETRANSMISSION_TIMEOUT)
389                         break;
390                 startElapsedTimer(&tmr,timeoutsecs*1000);
391                 while(!msecElapsed(&tmr)) {
392                         ep = EtherFromCache(Ip);
393                         if (ep)
394                                 break;
395
396                         pollethernet();
397                 }
398                 if (ep) {
399                         if (ecpy) {
400                                 memcpy((char *)ecpy, (char *)ep,6);
401                                 ep = ecpy;
402                         }
403                         return(ep);
404                 }
405                 RetransmitDelay(DELAY_INCREMENT);
406                 retry++;
407         }
408 #if INCLUDE_ETHERVERBOSE
409         if ((EtherVerbose & SHOW_ARP) && (retry))
410                 printf("  ARP giving up\n");
411 #endif
412         return(0);
413 }
414
415 /* processRARP();
416  *      Called by the fundamental ethernet driver code to process a RARP
417  *      request.
418  */
419 int
420 processRARP(struct ether_header *ehdr,ushort size)
421 {
422         struct  arphdr *arpp;
423
424         arpp = (struct arphdr *)(ehdr+1);
425         self_ecs(arpp->hardware);
426         self_ecs(arpp->protocol);
427         self_ecs(arpp->operation);
428
429         switch(arpp->operation) {
430         case RARP_RESPONSE:
431                 if (!memcmp((char *)arpp->targetha, (char *)BinEnetAddr,6)) {
432 #if INCLUDE_ETHERVERBOSE
433                         if (EtherVerbose & SHOW_ARP) {
434                                 printf("  RARP Response from %d.%d.%d.%d\n",
435                                     arpp->senderia[0],arpp->senderia[1],
436                                     arpp->senderia[2],arpp->senderia[3]);
437                                 printf("  MY IP: from %d.%d.%d.%d\n",
438                                     arpp->targetia[0],arpp->targetia[1],
439                                     arpp->targetia[2],arpp->targetia[3]);
440                         }
441 #endif
442                         memcpy((char *)BinIpAddr, (char *)arpp->targetia,6);
443                 }
444                 break;
445         case RARP_REQUEST:
446                 break;
447         default:
448                 printf("  Invalid RARP operation: 0x%x\n",arpp->operation);
449                 return(-1);
450         }
451         return(0);
452 }
453
454 /* processARP();
455  *      Called by the fundamental ethernet driver code to process a ARP
456  *      request.
457  */
458 char *
459 processARP(struct ether_header *ehdr,ushort size)
460 {
461         struct  arphdr *arpp;
462
463         arpp = (struct arphdr *)(ehdr+1);
464         self_ecs(arpp->hardware);
465         self_ecs(arpp->protocol);
466         self_ecs(arpp->operation);
467
468         switch(arpp->operation) {
469         case ARP_REQUEST:
470                 if (!memcmp((char *)arpp->targetia, (char *)BinIpAddr,4)) {
471                         ArpStore(arpp->senderia,arpp->senderha);
472                         SendArpResp(ehdr);
473                 }
474                 break;
475         case ARP_RESPONSE:
476                 if (!memcmp((char *)arpp->targetia, (char *)BinIpAddr,4)) {
477                         ArpStore(arpp->senderia,arpp->senderha);
478                 }
479                 break;
480         default:
481                 printf("  Invalid ARP operation: 0x%x\n",arpp->operation);
482                 printPkt(ehdr,(int)size,ETHER_INCOMING);
483                 return((char *)0);
484         }
485         return((char *)(arpp + 1));
486 }
487
488 #endif