errno: 38 (Function not implemented) on a call to change sysctl value

Suvesh Pratapa picture Suvesh Pratapa · Jul 24, 2013 · Viewed 8.9k times · Source

I want to set some local sysctl parameters with my program and I followed the directions given here: http://www.linux.it/~rubini/docs/sysctl/

As an example, here's what I'm doing to set the value for /proc/sys/net/ipv6/conf/tun0/accept_ra. I just configured my tun0 interface prior to this call. (I verified that my interface is up and I was able to assign an IP address as well)

int path_len = 5;
int tun0_accept_ra_path[] =  { CTL_NET,
                               NET_IPV6,
                               NET_IPV6_CONF,
                               ifr6.ifr6_ifindex, // This ifindex comes from an interface configured above
                               NET_IPV6_ACCEPT_RA };
int tun0_accept_ra_value = 0;
if (sysctl(tun0_accept_ra_path,
           path_len,
           NULL,
           0,
           &tun0_accept_ra_value,
           sizeof(tun0_accept_ra_value)) < 0) {
  printf("set sysctl 'accept_ra' failed. errno: %d\n", errno);
}

I get: set sysctl 'accept_ra' failed. errno: 38 Function not implemented

Any thoughts as to what could be wrong? I'm running as sudo so I don't think I should have access privilege issues.

I'm running Debian GNU/Linux 7.0 (wheezy) on a raspberry pi.

Answer

Giuseppe Pes picture Giuseppe Pes · Jul 25, 2013

The tutorial you used is too old! if you want a reference for this system call you should check out this link : http://man7.org/linux/man-pages/man2/sysctl.2.html

You get SIGSYS error because the sysctl does not exist. The correct name for that system is *_syscall* and glibc does not provide a wrapper becuase this syscall should not be called (it will be removed from the next Linux versions). You can be achieve the same result using the /proc interface.

If you want to keep your solution, you should change the code as follows ( I have not tested ,sorry):

#include <sys/syscall.h> 

int path_len = 5;
int tun0_accept_ra_path[] =  { CTL_NET,
                               NET_IPV6,
                               NET_IPV6_CONF,
                               ifr6.ifr6_ifindex, 
                               NET_IPV6_ACCEPT_RA };
int tun0_accept_ra_value = 0;
if (syscall(__NR_sysctl, 
           tun0_accept_ra_path,
           path_len,
           NULL,
           0,
           &tun0_accept_ra_value,
           sizeof(tun0_accept_ra_value)) < 0) {
  printf("set sysctl 'accept_ra' failed. errno: %d\n", errno);
}

A better solution may be :

int tun0_accept_ra_value = 0;

if ((fd=open("/proc/sys/net/ipv6/conf/tun0/accept_ra", O_RDWR)) < 0) 
   perror("OPEN"); 

if (write (fd, &tun0_accept_ra_value, sizeof(int)) < 0)
   perror("WRITE"); 

close(fd); 

I hope I have been useful.