SIOCSARP: Invalid Argument.

Hello everybody,
I've been programming an alternative to linux's standard 'arp' program. I can delete arp entries (SIOCDARP), get arp entries (SIOCGARP), but i'm having troubles setting entries with ioctl.

I can't set any PERM, USETRAILERS, or COM address. It only adds PUB entries and i don't know why.

This is the code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if_arp.h>

char *mac_ntoa(unsigned char *ptr){
static char address[30];

sprintf(address, "%02X:%02X:%02X:%02X:%02X:%02X",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);

return(address);
} /* End of mac_ntoa */

void mac_aton(char *buffer, char *pointer){
int i;
char c, val;
for(i = 0; i < 6; i++){
if(!(c = tolower(*pointer++))){
fprintf(stderr, "Invalid MAC Address.\n");
exit(-1);
        }/* End of If*/
if(isdigit(c)){
val = c - '0';
        }/* End of If*/
else if(c >= 'a' && c <= 'f'){
val = c - 'a' + 10;
        }/* End of Else If*/
else{
fprintf(stderr, "Invalid MAC Address.\n");
exit(-1);
        }/* End of Else */
if(isdigit(c)){
val = c - '0';
        } /* End of If*/
else if(c >= 'a' && c <= 'f'){
val = c - 'a' + 10;
        } /* End of Else If*/
else{
fprintf(stderr, "Invalid MAC Address.\n");
exit(-1);
        } /*End of Else*/
*buffer++ |= val; /* Store into buffer */
if(*pointer == ':'){
pointer++;
        } /* End of If */
} /* End of For */
} /* End of mac_aton */


int main(int argc, char* argv[]){
int s;

struct arpreq req;
struct hostent *hp;
struct sockaddr_in *sin;

char *host = argv[1];

bzero((caddr_t)&req, sizeof(req));
sin = (struct sockaddr_in *)&req.arp_pa;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(host);
if(sin->sin_addr.s_addr ==-1){
if(!(hp = gethostbyname(host))){
fprintf(stderr, "arp: %s ", host);
herror((char *)NULL);
return(-1);
}
bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 
sizeof(sin->sin_addr));
}
strcpy(req.arp_dev, "eth0");
mac_aton(req.arp_ha.sa_data, argv[2]);

argc -=2;
argv +=2;

req.arp_flags = ATF_PERM;

while(argc-- > 0){
if(!(strncmp(argv[0], "temp", 4))){
req.arp_flags &= ~ATF_PERM;
    } else if (!(strncmp(argv[0], "pub", 3))){
req.arp_flags |= ATF_PUBL;
    } else if (!(strncmp(argv[0], "trail", 5))){
req.arp_flags |= ATF_USETRAILERS;
}
argv++;
}/* End of While */

if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
perror("socket() failed.");
exit(-1);
}

if(ioctl(s, SIOCSARP, (caddr_t)&req) <0){
perror(host);
exit(-1);
}
printf("added.\n");
close(s);
return(0);
}

when i run it, it gives me the following error:

root@host:~# ./a.out 192.168.0.2 AA:BB:CC:DD:EE:FF temp
192.168.0.2: Invalid argument

The only entry which it adds is of type "pub".

Can someone tell me where is the error, or what am i missing?
Thanks in advance.

Try this modification to your code:

    ....
    req.arp_flags = ATF_PERM | ATF_COM;

    while (argc-- > 0) {
        if (!(strncmp(argv[0], "temp", 4))) {
            req.arp_flags &= ~ATF_PERM;
        } else if (!(strncmp(argv[0], "pub", 3))) {
            req.arp_flags |= ATF_PUBL;
        } else if (!(strncmp(argv[0], "priv", 4))) {
            req.arp_flags &= ~ATF_PUBL;
        } else if (!(strncmp(argv[0], "trail", 5))) {
            req.arp_flags |= ATF_USETRAILERS;
        }
        argv++;
    }
    .....

Hey fpmurphy, thanks for your quick reply. I made some changes in the code and got it working.

I think the errors were on mac_aton() function, the way i used the flags and in the lack of a defined device (arp_dev[16] in the arpreq structure).

With all of that changed, this is the working code, i think it would be of help for someone:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <net/if.h>
#include <errno.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/socket.h>

char *mac_ntoa(unsigned char *ptr){
static char address[30];

sprintf(address, "%02X:%02X:%02X:%02X:%02X:%02X",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
return(address);
} /* End of mac_ntoa */

int mac_aton(char *addr, unsigned char *ptr){
int i, v[6];
if((i = sscanf(addr, "%x:%x:%x:%x:%x:%x", &v[0], &v[1], &v[2], &v[3],
&v[4], &v[5])) !=6){
fprintf(stderr, "arp: invalid Ethernet address '%s'\n", addr);
return(1);
        } /* End of If*/
for(i = 0; i < 6; i++){
ptr = v;
        } /* End of For */
return(0);
}

int main(int argc, char* argv[]){

if(argc < 3 || argc > 4){
fprintf(stderr, "usage: %s <ip_addr> <hw_addr> [temp|pub|perm|trail]\n",
argv[0]);
fprintf(stderr, "default: temp.\n");
exit(-1);
        } /* End of If */

int s, flags;
char *host = argv[1];

struct arpreq req;
struct hostent *hp;
struct sockaddr_in *sin;

bzero((caddr_t)&req, sizeof(req)); /* caddr_t is not really needed. */

sin = (struct sockaddr_in *)&req.arp_pa;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(host);

if(sin->sin_addr.s_addr ==-1){
if(!(hp = gethostbyname(host))){
fprintf(stderr, "arp: %s ", host);
herror((char *)NULL);
return(-1);
        } /* End of If */
bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, sizeof(sin->sin_addr));
        } /* End of If */

if(mac_aton(argv[2], req.arp_ha.sa_data)){ /* If address is valid... */
return(-1);
}

argc -=2;
argv +=2;

flags = ATF_PERM | ATF_COM;

while(argc-- > 0){
if(!(strncmp(argv[0], "temp", 4))){
flags &= ~ATF_PERM;
        } else if(!(strncmp(argv[0], "pub", 3))){
flags |= ATF_PUBL;
        } else if(!(strncmp(argv[0], "trail", 5))){
flags |= ATF_USETRAILERS;
        } else if(!(strncmp(argv[0], "dontpub", 7))){ /* Not working yet */
flags |= ATF_DONTPUB;
        } else if(!(strncmp(argv[0], "perm", 4))){
flags = ATF_PERM;
        } else {
flags &= ~ATF_PERM;
        } /* End of Else*/
argv++;
        }/* End of While */

req.arp_flags = flags; /* Finally, asign the flags to the structure */
strcpy(req.arp_dev, "eth0"); /* Asign the device.  */

if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
perror("socket() failed.");
exit(-1);
        } /* End of If */

if(ioctl(s, SIOCSARP, (caddr_t)&req) <0){ /* caddr_t not really needed. */
perror(host);
exit(-1);
        } /* End of If */

printf("ARP cache entry successfully added.\n");
close(s);
return(0);
}

Thank you very much for your help. :b: