Getting MAC Address- get_hwaddr
Hey *myself* *chucles*,
Well, its what I promised, finding out the ethernet address, and all I say is that its really quite lame. ah well, anyways, before we get into that, some other things I learnt (actually, learnt alot, but this is 1 of the newer more relevent things)-
Modifications to program:
Well, last time I posted a program that would give you the list of interfaces right? Well, there, I outputted the raw ip address, ie, the numerical host order ip address (yes, cause its getting it off the interface). So, here is the modification I made (you should know where it is, right?)-
...
int temp=(address_list[i].addr);
char str[INET_ADDRSTRLEN+1];
inet_ntop(AF_INET,&(temp),str,INET_ADDRSTRLEN);
prinf("Device: %s ,\t Address: %s\n",(address_list[i]).device,ipaddr);
....
Now compile the code and run it… Yup, you’d get a nice output of the IP address.
Now, some of you might be asking: Hey, I thought IP address was at a higher layer, right? Well, ya, same doubt. But then I realised that ioctl fetches all the required information from that socket/interface. It doesnt depend on what level your asking. The socket file descriptor is associated with numerous things, like IP address, device hardware address, interface index (the device desriptor kinda, to the linux kernel), device name (human readable), etc. Btw, remember that this “ip address” which we get is the unicast IP address. It will be shown as a different ip address if the device is used in broadcast mode.
Modifications to my teaching style:
Well, the version of libnet that I have in source is quite old really. And a really bad part is that till now, it was perhaps only source of information. But nope, not anymore
. I found a library on sourceforge, called libdnet, which does essentially similar things like libnet, except that libdnet is in active development, and actually has a working site backing it. So from now on, although I will still “de-assemble” libnet, I will use libdnet as a reference as well.
De-assemble libnet_get_hwaddr:
Pre-requisites are less. All that is known is that how to use ioctl SIOCGIFHWADDR, which is very similar to how we used SIOCGIFADDR to find the logical address associated with that interface socket.
Pre-requisite’d code:
1. struct ether_addr definition: Although it isnt totally necessary, yet, its better we mention it anyways.
struct ether_addr{
u_int8_t ether_addr_octet[ETH_ALEN];
}; //u_int8_t is a typedef of unsigned char.
Well, um… thats it. Other than that, the pre-requisites of the previous blog entry, ie, the ifreq structure.
Code Dissection
struct ether_addr *
libnet_get_hwaddr(struct libnet_link_int *l, const u_char *device, char *ebuf)
{
The libnet_get_hwaddr accepts a “libnet_link_int” pointer… Well don’t worry, this thing is just retarded, cause its not used anywhere. You could well have passed an empty generic pointer cast to libnet_link_int . However, I suspect the reason for doing this. For future compatibility: The libnet_link_int contains the type of link layer protocol which could be used by the device. Perhaps, to make this function more generalised, this argument was passed, so that depending on the type of protocol used, the corresponding hard ware address should be returned… probably for some future edition.
As for the current moment, no, we don’t need it. So lets ignore it.
int fd;
struct ifreq ifr;
struct ether_addr *eap;
/*
* XXX - non-re-entrant!
*/
static struct ether_addr ea;
ifreq structure object ifr is used to query the kernel using the ioctl. It is the place where the result is returned. The ether_addr is a temporary object pointer, and it is passed back to the calling function (remember, the return value is a pointer). It gets its value from ifr object. The last struct ea is an object of ether_addr. The *eap is just a pointer to the ea object, kinda like an alias. ea is the name of da memory location. eap is just a pointer to it. So in the end, ea is returned back.
/*
* Create dummy socket to perform an ioctl upon.
*/
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
sprintf(ebuf, "get_hwaddr: %s", strerror(errno));
return (NULL);
}
The comments are appropriate. We need a sample socket to query for the ioctl, to act as the resource for ioctl. It could be of any type, even PACKET if we wanted it. Remember, btw, ideally, that AF_INET should be PF_INET, cause socket’s first argument is protocol family, not address family, but since 1 PF=1 AF, they are equivalent. In the end, just do some lame error checking to make sure that socket didnt fail (It won’t normally anyways)
memset(&ifr, 0, sizeof(ifr));
eap = &ea;
strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
Just empty the ifr object in the first line… its always better to, before passing it off to ioctl. remember, that some members of the ioctl union are smaller than union max size, so if we don’t do this, some other part of the union might be filled by junk, so we will have problems if we try access other members of the union (which would be foolish, and 0′ing still won’t do much good
. Maybe its in case sockaddr is not filled completely ). The second line is just making sure that ea Pointer points to ea (I find this damn foolish really, oh well!). The last part seems trivial but is important. It copies the device name to the ifr obj. That way, ioctl realises which device’s hard ware address to find, resourced by the socket file descriptor (the socket file descriptor points to a number of devices, so this is a must). I doubt whether filling any other thing, like interface name would have the same effect (the union is prob Ignored while reading ifr object, since kernel doesnt know which information we provided. for eg, for 1 value of ifindex, the corresponding logical address might also be in a proper format, so kernel would be confused on which to operate. However, union filling would be done if you had to SET an hardware address, by SIOSGIFHWADDR (note the S instead of C), so the kernel knows where to look. Actually, this is what probably is done, right? Else how would u supply the new values anyways?)
if (ioctl(fd, SIOCGIFHWADDR, (char *)&ifr) < 0)
{
close(fd);
sprintf(ebuf, "get_hwaddr: %s", strerror(errno));
return (NULL);
}
This is the code which does the main part. It gets the hard ware address. As always, ioctl takes the socket file descriptor in the first argument. The second is the request: SIOCGIFHWADDR – Catch the Hardware address. The next argument that it takes is a pointer to ifreq (but since ioctl takes only character arguments, it must be cast). Note that ifr.ifr_name should be filled with the correct device name (no extra spaces, not deleted 0s or anything… it should match ditto ditto. If not, check libnet_ifaddrlist program last time’s blog). If the device is filled, and valid, and if you have proper priviledges (perhaps) then this ioctl call neednt fail. But if it does, libnet does the error checking. If all went correctly, the hard ware address should be stored in the ifr_hwaddr sockaddr struct’s sa_data .
What confused me earlier was the format in which it would be returned. It turns out that there is no formatting, the raw mac address is passed (yup, 1->1 bits). So, sa_data should be filled only with 6 bytes of data, cause mac addresses are 6 bytes.
memcpy(eap, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
close(fd);
return (eap);
}
This is the last part, quite simple. It does a 1 to 1 memory copy from ifr_hwaddr. Remember that ethernet (mac) addresses are 6 bytes, so you will have to say 6 as last parameter (ETHER_ADDR_LEN is a macro of libnet). The first arg is a pointer to the object of ether_addr , &ea. The second is the actual data, which was retrieved from ioctl.
Well, thats it, we discussed how to get the mac address.
Usage
Yeah, this is how you could use what you learnt to learn about a device’s mac address.
#include "libnethead.h"
int main(){
//Let us get the ethernet hw address from the supplied device name.
int fd,i;
struct ether_addr ea;
struct ifreq ifr;
char buf[3];
printf("Enter the device name: ");
scanf("%s",ifr.ifr_name);
//lets init a nice socket
fd=socket(AF_INET,SOCK_DGRAM,0);
if(ioctl(fd,SIOCGIFHWADDR,(char*)&ifr)<0){ close(fd); printf("Error\n"); exit(0); }
memcpy(&ea,&ifr.ifr_hwaddr.sa_data,6); //Ethernet address is 6
for(i=0;i<6;i++){
sprintf(buf,"%x",ea.ether_addr_octet[i]);
if(strlen(buf)<2){ printf("0"); } //If there is preceeding 0, print it out
printf("%s",buf);
if(i!=5) printf(":"); //The colon in between the output.
}
printf("\b\n");
return 0;
}
As usual, we use the header we made last time, libnethead.h . As you see, most part of the code was just initialising, getting input, and then correctly formatting and stupid stuff like that… Nothing real meaty (slurp)!
Oh, btw, forgot to mention, that although I used memcpy to ea, you couldve done it better, just memcpy to a char hwaddr[IFNAMSIZ]; . I just wanted to show u the concepts used in the libnet script. Eventually, ether_addr_octet is just a character string itself.
Whats next:
Good question. There are a few functions resembling getting the ethernet hwaddr, so I think I’ll just skim through some of them, in the next blog. That will be up soon. Probably the next (true) 1 will be totally different from what we did till now. Next time, we will create a function to make a nice little (empty) ethernet packet. I was in conflict to show how to open a socket for reading and writing to the line, but decided against it, since it would be no use if we don’t know what to send, right?
About this entry
You’re currently reading “Getting MAC Address- get_hwaddr,” an entry on Crack in the River
- Published:
- July 14, 2007 / 5:38 am
- Category:
- Libnet De-assembled
- Tags:
No comments yet
Jump to comment form | comments rss [?] | trackback uri [?]