I want to get an entry from the ARP cache on Linux 2.2.4 using ioctl().
I always get this error message :
ioctl: Operation not supported by device
I run the program as root. Here is the code, I don't know what's wrong.
Any idea ?
---------------------------------
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
void main(void) {
int sockfd;
struct arpreq myarp;
struct sockaddr_in *sin;
struct in_addr inaddr;
sockfd = socket(AF_INET,SOCK_DGRAM, 0);
inaddr.s_addr = inet_addr("10.10.10.1");
sin = (struct sockaddr_in *) &myarp.arp_pa;
bzero(sin, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
memcpy(&sin->sin_addr, &inaddr, sizeof(struct in_addr));
if((ioctl(sockfd, SIOCGARP, &myarp)) != 0) {
perror("ioctl");
exit(0);
}
}
-----------------------------------------
Thanks
Fred
Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.
> int sockfd;
> struct arpreq myarp;
> struct sockaddr_in *sin;
> struct in_addr inaddr;
>
> sockfd = socket(AF_INET,SOCK_DGRAM, 0);
>
> inaddr.s_addr = inet_addr("10.10.10.1");
You are missing the declaration for this function. It is declared in
<arpa/inet.h>. The compiler-generated implicit declaration will not do because
the return value is not int! Use the -Wall option of gcc.
Note that this method of initializing a struct in_addr is obsolete. It's better
to do
if (inet_aton(some_addr_string, &inaddr) == 0) {
/* address string is bad */
}
This method can handle the address 255.255.255.255 without interpreting it as
error, and keeps the representation of struct in_addr opaque.
> sin = (struct sockaddr_in *) &myarp.arp_pa;
> bzero(sin, sizeof(struct sockaddr_in));
Use ANSI C memset(). bzero is an obsolete BSD-ism.
> sin->sin_family = AF_INET;
>
> memcpy(&sin->sin_addr, &inaddr, sizeof(struct in_addr));
ANSI C supports structure assignment. You can assign one struct in_addr
to another:
sin->sin_addr = inaddr;
You could write all of this more simply as:
struct sockaddr_in sin = { 0 };
struct arpreq myarp = { 0 };
sin.sin_family = AF_INET;
inet_aton("10.10.10.1", &sin.sin_addr);
memcpy(&myarp.arp_pa, &sin, sizeof myarp.arp_pa);
strcpy(myarp.arp_dev, "eth0");
> if((ioctl(sockfd, SIOCGARP, &myarp)) != 0) {
> perror("ioctl");
> exit(0);
> }
How about initializing the rest of that arpreq structure? There is, for
example, a device field (arp_dev) that you left uninitialized.
You might want to add an initializer to the declaration of myarp, like
struct arpreq myarp = { 0 }; /* zero out all fields */
This way, any char[] fields are empty strings, ints are zeros,
pointers are NULL, doubles are 0.0 etc.
The arp_dev field is not optional. If it is an empty string, the
SIOCGARP ioctl() will immediately return -ENODEV. Here is the 2.2.9 code
that does it:
if (r.arp_dev[0]) {
err = -ENODEV;
if ((dev = dev_get(r.arp_dev)) == NULL)
goto out;
/* Mmmm... It is wrong... ARPHRD_NETROM==0 */
if (!r.arp_ha.sa_family)
r.arp_ha.sa_family = dev->type;
err = -EINVAL;
if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type)
goto out;
} else if (cmd == SIOCGARP) {
err = -ENODEV;
goto out;
}
Here is a complete working program:
#include <stdlib.h>
#include <stdio.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct sockaddr_in sin = { 0 };
struct arpreq myarp = { { 0 } };
int sockfd;
sin.sin_family = AF_INET;
inet_aton("10.10.10.1", &sin.sin_addr);
memcpy(&myarp.arp_pa, &sin, sizeof myarp.arp_pa);
strcpy(myarp.arp_dev, "eth0");
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
return EXIT_FAILURE;
}
if (ioctl(sockfd, SIOCGARP, &myarp) == -1) {
perror("ioctl");
return EXIT_FAILURE;
}
/* do something with ioctl() results */
return EXIT_SUCCESS;
}
Thanks for your complete and quick answer, but it's still not working..
Using your code, I get now this error message :
ioctl: Device not configured
> Here is a complete working program:
>
> #include <stdlib.h>
> #include <stdio.h>
> #include <net/if_arp.h>
> #include <sys/ioctl.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
>
> int main(void)
> {
> struct sockaddr_in sin = { 0 };
> struct arpreq myarp = { { 0 } };
> int sockfd;
>
> sin.sin_family = AF_INET;
> inet_aton("10.10.10.1", &sin.sin_addr);
>
> memcpy(&myarp.arp_pa, &sin, sizeof myarp.arp_pa);
> strcpy(myarp.arp_dev, "eth0");
>
> if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
> perror("socket");
> return EXIT_FAILURE;
> }
>
> if (ioctl(sockfd, SIOCGARP, &myarp) == -1) {
> perror("ioctl");
> return EXIT_FAILURE;
> }
>
> /* do something with ioctl() results */
>
> return EXIT_SUCCESS;
> }
>
I have the impression that there is some kind of mismatch between errnos and
the messages in glibc2.
The program runs fine on my glibc2/linux 2.2.9 system. If I change
eth0 to some non-existent ethernet, I get errno 19, which the library
interprets as ``operation not supported by device''. But according
to the kernel header file, the error is ENODEV, which is ``No such device''.
Glibc2 is emitting misleading error messages.
If I change the sought-after IP address to something that isn't in the
ARP cache, then I get error 6, with the ``Device not configured'' message.
>ioctl: Device not configured
This is what glibc2 yields for ENXIO, which is actually ``No such device or
address''. Neither error message accurately represents the actual error, though
``no address'' is closer in meaning.
The best thing to do with exotic kernel ioctl's is to look at the kernel
code, and trap all possible errors and convert them to meaningful
error messages or actions in your program. Don't rely on strerror()
because even if the message mapping is accurate, the generic messages
often don't describe the error condition accurately. In the
case of ENXIO returned from the SIOCGARP, you really want the message
``no hardware address found in the device's ARP cache corresponding
to the given network address''.
Thanks again for your help.
Fred
In article <slrn7ltdh...@ashi.FootPrints.net>,