如何在Linux中从C设置IP地址
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6652384/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to set the IP address from C in linux
提问by Allan
By using strace and ifconfig, I found that I can set the IP address this way:
通过使用strace和ifconfig,我发现我可以这样设置IP地址:
#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?
这样做的“正确”方法是什么?
采纳答案by grep
The "correct" way for IPv4 without magic +2:
没有魔法+2的IPv4的“正确”方式:
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
要使用 IPv6,请将其强制转换为 sockaddr_in6
回答by MarkR
The "correct" way to do it is to spawn a copy of the iproute2 "ip" program (in /sbin/ip ) with relevant parameters.
执行此操作的“正确”方法是生成具有相关参数的 iproute2“ip”程序(在 /sbin/ip 中)的副本。
the ioctl interface is generally obsolescent and doesn't allow you to configure all parameters (for example, un-named IP aliases).
ioctl 接口通常已经过时,并且不允许您配置所有参数(例如,未命名的 IP 别名)。
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.
即使是像 dhcpcd 这样需要更改 IP 地址的守护进程,通常也是通过生成外部程序来实现的……这不像您会经常这样做。
回答by Mahdi Mohammadi
#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");