By using strace and ifconfig, I found that I can set the IP address this way:
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>
int main(int argc, const char *argv[]) {
struct ifreq ifr;
const char * name = "eth1:0";
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
inet_pton(AF_INET, "10.12.0.1", ifr.ifr_addr.sa_data + 2);
ioctl(fd, SIOCSIFADDR, &ifr);
inet_pton(AF_INET, "255.255.0.0", ifr.ifr_addr.sa_data + 2);
ioctl(fd, SIOCSIFNETMASK, &ifr);
ioctl(fd, SIOCGIFFLAGS, &ifr);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
ioctl(fd, SIOCSIFFLAGS, &ifr);
return 0;
}
Now I want to add multiple ip addresses on eth1:0. eth1:0 is the 'fake' interface created to add more addresses. Man page of ioctl shows to set and get ip address but is it possible to add multiple ip addresses using ioctl? If so how can I add?
Related
I'm trying to create a tunnel in C and im struggling to set its IP address up. I have an "invalid argument" error in ioctl with SIOCSIFADDR argument. Can someone explain me how to set up the IP address if this function doesn't work with tun ?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if_tun.h>
#include <linux/if.h>
#include <fcntl.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define IP_DEST "127.0.0.1"
#define IP_SOURCE "127.0.0.1"
int tun_alloc(char* dev) {
//dev will store the name of the tun created
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
//open the clone device
if( (fd = open(clonedev, O_RDWR)) < 0 ) {
printf("Error opening directory");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN;
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
//Create the TUN
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
close(fd);
printf("Error creating the tun");
return err;
}
strcpy(dev, ifr.ifr_name);
return fd;
}
int main(int argc, char* argv[]) {
//Creating the TUN interface
char tun_name[IFNAMSIZ];
strncpy(tun_name, "tun3", IFNAMSIZ);
int tunfd = tun_alloc(tun_name);
if (tunfd < 0) {
perror("tun_create");
return 1;
}
printf("TUN interface %s created\n", tun_name);
//Setting its IP Adress
struct ifreq ifr;
struct sockaddr_in addr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);
addr.sin_family = AF_INET;
if(inet_pton(AF_INET,IP_DEST,&(addr.sin_addr))<0){
fprintf(stderr,"ERROR with the IP address");
return 1;
};
memcpy(&(ifr.ifr_addr), &addr, sizeof (struct sockaddr));
if (ioctl(tunfd, SIOCSIFADDR, &ifr) < 0) {
perror("ioctl");
exit(1);
}
printf("TUN interface %s set IP address to %s\n", tun_name, IP_DEST);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if(ioctl(tunfd, SIOCSIFFLAGS, &ifr)<0){
perror("ioctl");
exit(1);
};
printf("TUN running");
return 0;
}
I don't really understand how iotcl works and the documentation hasn't helped me for tun interfaces.
The SIOCSIFADDR and SIOCSIFFLAGS (and other SIOwhatever) ioctl calls should be made towards any AF_INET socket, not towards the tun interface itself. The design is so that you can use them to configure any interface, not only the one you created. You have to create an AF_INET socket and then call ioctl on that socket.
If you think it's a bit weird that you have to make a socket, you're right. ioctl is used to make "special requests" about files, other than read or write. The request is interpreted depending on the type of file. Using a socket makes sure it is interpreted by the IP networking system as a request relating to IP sockets. It would make logical sense that you could also make IP-related special requests on tun interfaces, but apparently the kernel developers didn't think of that, and there is no need for them to work since you can just create a socket.
There is another way to set IP addresses using a system called "rtnetlink", but it is more complicated and unnecessary for a simple scenario like this.
I use this code to setup the IP address
int
set_ip(const char *name, const char *ip)
{
struct ifreq ifr;
struct sockaddr_in *addr;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
addr = (struct sockaddr_in*)&ifr.ifr_addr;
/* inet_pton() returns 1 on success */
/* network address was successfully converted */
int s;
s = inet_pton(AF_INET, ip, &addr->sin_addr);
if (s <= 0) {
if (s == 0)
fprintf(stderr, "Set IP %s not in presentation format\n", ip);
else
perror ("inet_pton");
exit (EXIT_FAILURE);
}
ioctl(fd, SIOCSIFADDR, &ifr);
ioctl(fd, SIOCGIFFLAGS, &ifr);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
ioctl(fd, SIOCSIFFLAGS, &ifr);
return 0;
}
The code is modified from here How to set the IP address from C in linux.
My question is
How to know the ip address change successfully or not? Because the return value of ioctl always be 0.
$ uname -a
Linux DMA1 4.4.0-38-generic #57-Ubuntu SMP Tue Sep 6 15:42:33 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
I have googled for hours but not found any solutions. So I post my question in here to ask experts in SO. Thanks in advance
Thank you all, below is the solution suggested by you experts:
#include <stdio.h>
#include <stdlib.h>
#include <ifaddrs.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/route.h> // struct rtentry
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int
set_addr (const char *name, const char *ip, unsigned long request)
{
struct ifreq ifr;
struct sockaddr_in *addr;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
addr = (struct sockaddr_in*)&ifr.ifr_addr;
// inet_pton() returns 1 on success
// network address was successfully converted
int s;
s = inet_pton(AF_INET, ip, &addr->sin_addr);
if (s <= 0) {
if (s == 0)
fprintf(stderr, "Set IP %s not in presentation format\n", ip);
else
perror ("inet_pton");
exit (EXIT_FAILURE);
}
if (ioctl(fd, request, &ifr) != 0) { // SIOCSIFADDR
perror ("ioctl");
exit (EXIT_FAILURE);
}
ioctl(fd, SIOCGIFFLAGS, &ifr);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
ioctl(fd, SIOCSIFFLAGS, &ifr);
return 0;
}
Don't forget to grant your program with root privilege using following commands in your Makefile or command line:
$ sudo chown root your_program
$ sudo chmod a+xs your_program
Otherwise, you will get a error message ioctl: Operation not permitted
I am trying to implement a simple client and server in C and I can't find online an example how to set a specific IP address to the client. This is what I got so far:
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
<some code to handle error>
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(<addressOfTheServer>);
address.sin_port = htons(<portToConnectToServer>);
len = sizeof(address);
int result = connect(sockfd, (struct sockaddr *)&address, len);
On the server side I check for the client IP Address and I always get 127.0.0.1
I want to change it something different.
If you want your client to connect using a specific network interface (say, because you have multiple network cards), then you first need to call bind(2) on that interface's IP address before connecting. For example, if you have two network interfaces with IP addresses 192.168.1.100 and 10.101.151.100, then to connect using the 192.168.1.100 address you could do this:
// Error checking omitted for expository purposes
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// Bind to a specific network interface (and optionally a specific local port)
struct sockaddr_in localaddr;
localaddr.sin_family = AF_INET;
localaddr.sin_addr.s_addr = inet_addr("192.168.1.100");
localaddr.sin_port = 0; // Any local port will do
bind(sockfd, (struct sockaddr *)&localaddr, sizeof(localaddr));
// Connect to the remote server
struct sockaddr_in remoteaddr;
remoteaddr.sin_family = AF_INET;
remoteaddr.sin_addr.s_addr = inet_addr(server_ip);
remoteaddr.sin_port = htons(server_port);
connect(sockfd, (struct sockaddr *)&remoteaddr, sizeof(remoteaddr));
OK so I put the solution together with getting the ip address off of the computer as well:
/*dl_senderprog.c - debian linux send to server a client, datagram*/
/***********************************************************************
140203 lets see if we can bind to a port
ts7500:/var/www/jon/uvir_sensor_lab/source/socket#
ts7500:/var/www/jon/uvir_sensor_lab/source/socket# vi senderprog_bind.c
ts7500:/var/www/jon/uvir_sensor_lab/source/socket# gcc -g senderprog_bind.c -o senderprog_bind
ts7500:/var/www/jon/uvir_sensor_lab/source/socket# ./senderprog_bind
Sender:Client-Usage: ./senderprog_bind <hostname> <message>
ts7500:/var/www/jon/uvir_sensor_lab/source/socket#
ts7500:/var/www/jon/uvir_sensor_lab/source/socket#
ts7500:/var/www/jon/uvir_sensor_lab/source/socket# ./senderprog_bind 10.0.1.26 "dot,33,22"
MY IP address:10.0.1.242: on port: 1043
Sender: Client-gethostname() is OK...
Sender: Client-socket() sockfd is OK...
Sender: Using port: 14950
Sender: Client-sendto() is OK...
Sender: sent 9 bytes to 10.0.1.26
Sender: Client-sockfd successfully closed!
ts7500:/var/www/jon/uvir_sensor_lab/source/socket#
ts7500:/var/www/jon/uvir_sensor_lab/source/socket#
ts7500:/var/www/jon/uvir_sensor_lab/source/socket# # it worked!!!!!
ts7500:/var/www/jon/uvir_sensor_lab/source/socket#
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>
/* the port users will be connecting to 14950 is the port on the windows machine
that I have the server running on */
#define TOPORT 14950
#define MYPORT 1043
void my_ip( char *myniccard, char *myipaddr) {
int fd;
struct ifreq ifr;
myipaddr[0]=0;
fd = socket(AF_INET, SOCK_DGRAM, 0);
/* I want to get an IPv4 IP address */
ifr.ifr_addr.sa_family = AF_INET;
/* I want IP address attached to "eth0" */
//strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);
strncpy(ifr.ifr_name, myniccard, IFNAMSIZ-1);
ioctl(fd, SIOCGIFADDR, &ifr);
close(fd);
/* display result */
sprintf(myipaddr,"%s"
, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
printf("MY IP address:%s: on port: %d\n", myipaddr, MYPORT);
} // my_ip
int main(int argc, char *argv[ ])
{
int sockfd;
/* connectors address information */
struct sockaddr_in their_addr;
struct sockaddr_in localaddr;
char myipaddressm[22]; //buffer for ip address
char *myniccardm ="eth0"; // check with ipconfig for correct ethernet port
struct hostent *he;
int numbytes;
if (argc != 3) {
fprintf(stderr, "Sender:Client-Usage: %s <hostname> <message>\n", argv[0]);
exit(1);
}
my_ip(myniccardm, myipaddressm);
/* get the host info */
if ((he = gethostbyname(argv[1])) == NULL) {
perror("Sender: Client-gethostbyname() error lol!");
exit(1);
}
else
printf("Sender: Client-gethostname() is OK...\n");
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Sender: Client-socket() error lol!");
exit(1);
}
else
printf("Sender: Client-socket() sockfd is OK...\n");
// Bind to a specific network interface
// (this is unusual, as you normally do not want a specific
// port for the client, but we have a specific server in
// this case that will not accept connects unless its on
// a specific port )
localaddr.sin_family = AF_INET;
localaddr.sin_addr.s_addr = inet_addr(myipaddressm);
localaddr.sin_port = htons(MYPORT); // Any local port will do
bind(sockfd, (struct sockaddr *)&localaddr, sizeof(localaddr));
/* host byte order */
their_addr.sin_family = AF_INET;
/* short, network byte order */
printf("Sender: Using port: %d\n",TOPORT);
their_addr.sin_port = htons(TOPORT);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
/* zero the rest of the struct */
memset(&(their_addr.sin_zero), '\0', 8);
if((numbytes = sendto(sockfd, argv[2],
strlen(argv[2]),
0,
(struct sockaddr *)&their_addr,
sizeof(struct sockaddr))) == -1) {
perror("Sender: Client-sendto() error lol!");
exit(1);
}
else
printf("Sender: Client-sendto() is OK...\n");
printf("Sender: sent %d bytes to %s\n", numbytes, inet_ntoa(their_addr.sin_addr));
if (close(sockfd) != 0)
printf("Sender: Client-sockfd closing is failed!\n");
else
printf("Sender: Client-sockfd successfully closed!\n");
return 0;
}//main
/*******************************************EOF***********************/
I've run this on my debian linux embedded arm ts-7500 single board computer.
By using strace and ifconfig, I found that I can set the IP address this way:
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>
int main(int argc, const char *argv[]) {
struct ifreq ifr;
const char * name = "eth1";
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
inet_pton(AF_INET, "10.12.0.1", ifr.ifr_addr.sa_data + 2);
ioctl(fd, SIOCSIFADDR, &ifr);
inet_pton(AF_INET, "255.255.0.0", ifr.ifr_addr.sa_data + 2);
ioctl(fd, SIOCSIFNETMASK, &ifr);
ioctl(fd, SIOCGIFFLAGS, &ifr);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
ioctl(fd, SIOCSIFFLAGS, &ifr);
return 0;
}
But I am not very happy with this solution:
inet_pton(AF_INET, "10.12.0.1", ifr.ifr_addr.sa_data + 2);
What is the "right" way of doing this?
The "correct" way for IPv4 without magic +2:
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
inet_pton(AF_INET, "10.12.0.1", &addr->sin_addr);
To use IPv6, cast it to sockaddr_in6
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // close()
#include <string.h> // strcpy, memset(), and memcpy()
#include <netdb.h> // struct addrinfo
#include <sys/types.h> // needed for socket(), uint8_t, uint16_t
#include <sys/socket.h> // needed for socket()
#include <netinet/in.h> // IPPROTO_RAW, INET_ADDRSTRLEN
#include <netinet/ip.h> // IP_MAXPACKET (which is 65535)
#include <arpa/inet.h> // inet_pton() and inet_ntop()
#include <sys/ioctl.h> // macro ioctl is defined
#include <bits/ioctls.h> // defines values for argument "request" of ioctl.
#include <net/if.h> // struct ifreq
#include <linux/if_ether.h> // ETH_P_ARP = 0x0806
#include <linux/if_packet.h> // struct sockaddr_ll (see man 7 packet)
#include <net/ethernet.h>
#include <errno.h> // errno, perror()
#include <netinet/in.h>
#include <net/route.h>
/**
* Create socket function
*/
int create_socket() {
int sockfd = 0;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd == -1){
fprintf(stderr, "Could not get socket.\n");
return -1;
}
return sockfd;
}
/**
* Generic ioctrlcall to reduce code size
*/
int generic_ioctrlcall(int sockfd, u_long *flags, struct ifreq *ifr) {
if (ioctl(sockfd, (long unsigned int)flags, &ifr) < 0) {
fprintf(stderr, "ioctl: %s\n", (char *)flags);
return -1;
}
return 1;
}
/**
* Set route with metric 100
*/
int set_route(int sockfd, char *gateway_addr, struct sockaddr_in *addr) {
struct rtentry route;
int err = 0;
memset(&route, 0, sizeof(route));
addr = (struct sockaddr_in*) &route.rt_gateway;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(gateway_addr);
addr = (struct sockaddr_in*) &route.rt_dst;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr("0.0.0.0");
addr = (struct sockaddr_in*) &route.rt_genmask;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr("0.0.0.0");
route.rt_flags = RTF_UP | RTF_GATEWAY;
route.rt_metric = 100;
err = ioctl(sockfd, SIOCADDRT, &route);
if ((err) < 0) {
fprintf(stderr, "ioctl: %s\n", "mahdi MOAHMMADI Error");
return -1;
}
return 1;
}
/**
* Set ip function
*/
int set_ip(char *iface_name, char *ip_addr, char *gateway_addr)
{
if(!iface_name)
return -1;
struct ifreq ifr;
struct sockaddr_in sin;
int sockfd = create_socket();
sin.sin_family = AF_INET;
// Convert IP from numbers and dots to binary notation
inet_aton(ip_addr,&sin.sin_addr.s_addr);
/* get interface name */
strncpy(ifr.ifr_name, iface_name, IFNAMSIZ);
/* Read interface flags */
generic_ioctrlcall(sockfd, (u_long *)"SIOCGIFFLAGS", &ifr);
/*
* Expected in <net/if.h> according to
* "UNIX Network Programming".
*/
#ifdef ifr_flags
# define IRFFLAGS ifr_flags
#else /* Present on kFreeBSD */
# define IRFFLAGS ifr_flagshigh
#endif
// If interface is down, bring it up
if (ifr.IRFFLAGS | ~(IFF_UP)) {
ifr.IRFFLAGS |= IFF_UP;
generic_ioctrlcall(sockfd, (u_long *)"SIOCSIFFLAGS", &ifr);
}
// Set route
set_route(sockfd, gateway_addr , &sin);
memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
// Set interface address
if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
fprintf(stderr, "Cannot set IP address. ");
perror(ifr.ifr_name);
return -1;
}
#undef IRFFLAGS
return 0;
}
usage:
set_ip("eth0", "192.168.181.128", "192.168.181.1");
The "correct" way to do it is to spawn a copy of the iproute2 "ip" program (in /sbin/ip ) with relevant parameters.
the ioctl interface is generally obsolescent and doesn't allow you to configure all parameters (for example, un-named IP aliases).
Even daemons like dhcpcd which need to change the IP address, typically do it by spawning an external program... it's not like you're going to do it very often.
Hello: In the set_ip function, it gave me an error.
I had to change from :
inet_aton(ip_addr, &sin.sin_addr.s_addr);
to
inet_aton(ip_addr, &sin.sin_addr);
and worked well.
I am trying to get and set the IP address using the IOCTL interface on Linux.
I am successfully able to get and set it. When I set the ip address,
ifconfig eth0 shows a proper IP address, but then the system gets disconnected.
i.e. System is not pingable.
Here's my code for setting the IP address. Please let me know if I am missing something.
struct ifreq ifr;
in_addr_t in_addr;
struct sockaddr_in sin;
memset(&ifr, 0, sizeof(struct ifreq));
memset(&sin, 0, sizeof(struct sockaddr_in));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
sprintf(ifr.ifr_name, "eth0");
in_addr = inet_addr("192.168.101.17");
sin.sin_addr.s_addr = in_addr;
memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
io = ioctl(sockfd, SIOCSIFADDR, (char *)&ifr);
This will work for interfaces or aliases. Use "strace" to verify correct operation:
strace ./ifconfig
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
ioctl(5, SIOCSIFADDR, {ifr_name="eth0:8", ifr_addr={AF_INET, inet_addr("192.168.1.202")}}) = 0
ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0:8", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(5, SIOCSIFFLAGS, {ifr_name="eth0:8", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
close(5) = 0
Complete source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> /* offsetof */
#include <net/if.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_ether.h>
#endif
#define IFNAME "eth0:2"
#define HOST "192.168.1.204"
#define ifreq_offsetof(x) offsetof(struct ifreq, x)
int main(int argc, char **argv) {
struct ifreq ifr;
struct sockaddr_in sai;
int sockfd; /* socket fd we use to manipulate stuff with */
int selector;
unsigned char mask;
char *p;
/* Create a channel to the NET kernel. */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
/* get interface name */
strncpy(ifr.ifr_name, IFNAME, IFNAMSIZ);
memset(&sai, 0, sizeof(struct sockaddr));
sai.sin_family = AF_INET;
sai.sin_port = 0;
sai.sin_addr.s_addr = inet_addr(HOST);
p = (char *) &sai;
memcpy( (((char *)&ifr + ifreq_offsetof(ifr_addr) )),
p, sizeof(struct sockaddr));
ioctl(sockfd, SIOCSIFADDR, &ifr);
ioctl(sockfd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
// ifr.ifr_flags &= ~selector; // unset something
ioctl(sockfd, SIOCSIFFLAGS, &ifr);
close(sockfd);
return 0;
}
Perhaps you forgot to set the interface to up?
ioctl(sockfd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
ioctl(sockfd, SIOCSIFFLAGS, &ifr);