之前写过一个ifconfig版本的IP地址设置,但是不能设置保留的E类地址。最近看了下iproute的源码,移植了一下,现在E类IP地址也可以配了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_link.h>
#include <linux/if_addr.h>
#include <linux/neighbour.h>
#include <linux/netconf.h>
#define MAX_BUF_SIZE 32768
#define INFINITY_LIFE_TIME 0xFFFFFFFFU
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
typedef struct
{
__u16 flags;
__u16 bytelen;
__s16 bitlen;
__u16 family;
__u32 data[8];
} inet_prefix;
struct rtnl_handle
{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
int proto;
FILE *dump_fp;
#define RTNL_HANDLE_F_LISTEM_ALL_NSID 0x01
int flags;
};
struct rtnl_handle rth = { .fd = -1 };
void rtnl_close(struct rtnl_handle *rth)
{
if (rth->fd >= 0)
{
close(rth->fd);
rth->fd = -1;
}
}
int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
int protocol)
{
socklen_t addr_len;
int sndbuf = 32768;
int rcvbuf = 1024 * 1024;
memset(rth, 0, sizeof(*rth));
rth->proto = protocol;
rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
if (rth->fd < 0)
{
perror("Cannot open netlink socket!");
return -1;
}
if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))
< 0)
{
perror("SO_SNDBUF");
return -1;
}
if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))
< 0)
{
perror("SO_RECVBUF");
return -1;
}
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = subscriptions;
if (bind(rth->fd, (struct sockaddr *)&rth->local, sizeof(rth->local)) < 0)
{
perror("Cannot bind netlin socket");
return -1;
}
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr *)&rth->local, &addr_len) < 0)
{
perror("Cannot getsockname");
return -1;
}
if (rth->local.nl_family != AF_NETLINK)
{
fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
return -1;
}
rth->seq = time(NULL);
return 0;
}
int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
{
return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
struct nlmsghdr *answer, size_t maxlen)
{
int status;
unsigned seq;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = (void *) n,
.iov_len = n->nlmsg_len
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
};
char buf[MAX_BUF_SIZE];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
n->nlmsg_seq = seq = ++rtnl->seq;
if (answer == NULL)
{
n->nlmsg_flags |= NLM_F_ACK;
}
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0)
{
fprintf(stderr, "sendmsg error: %d - %s \n", errno, strerror(errno));
return -1;
}
memset(buf, 0, sizeof(buf));
iov.iov_base = buf;
while (1)
{
iov.iov_len = sizeof(buf);
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0)
{
if (errno == EINTR || errno == EAGAIN)
{
continue;
}
return -1;
}
if (status == 0)
{
return -1;
}
if (msg.msg_namelen != sizeof(nladdr))
{
return -1;
}
for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); )
{
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l < 0 || len > status)
{
return -1;
}
if (nladdr.nl_pid != 0 ||
h->nlmsg_pid != rtnl->local.nl_pid ||
h->nlmsg_seq != seq)
{
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
continue;
}
if (h->nlmsg_type == NLMSG_ERROR)
{
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
if (l < sizeof(struct nlmsgerr))
{
}
else if (!err->error)
{
if (answer)
{
memcpy(answer, h, ((maxlen) < (h->nlmsg_len) ?
(maxlen) : (h->nlmsg_len)));
}
return 0;
}
fprintf(stderr, "RTNETLINK answers: %s\n",
strerror(-err->error));
return -1;
}
if (answer)
{
memcpy(answer, h, ((maxlen) < (h->nlmsg_len) ? (maxlen) :
h->nlmsg_len));
return 0;
}
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC)
{
continue;
}
if (status)
{
fprintf(stderr, "!!!Remnat of size %d\n", status);
return -1;
}
}
}
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
{
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
{
return addattr_l(n, maxlen, type, &data, sizeof(__u32));
}
unsigned ll_name_to_index(const char *name)
{
unsigned index;
index = if_nametoindex(name);
printf("the index of %s is %d\n", name, index);
if (index == 0)
{
sscanf(name, "if%u", &index);
}
return index;
}
int get_addr_ipv4(__u8 *ap, const char *cp)
{
int i;
printf("Debug - ipv4: %s\n", cp);
for (i = 0; i < 4; i++)
{
unsigned long n;
char *endp;
n = strtoul(cp, &endp, 0);
if (n > 255)
{
return -1;
}
if (endp == cp)
{
return -1;
}
ap[i] = n;
if (*endp == '\0')
break;
if (i == 3 || *endp != '.')
{
return -1;
}
cp = endp + 1;
}
return 1;
}
int get_addr_1(inet_prefix *addr, const char *name, int family)
{
memset(addr, 0, sizeof(*addr));
if (strcmp(name, "default") == 0 ||
strcmp(name, "all") == 0 ||
strcmp(name, "any") == 0)
{
addr->family = family;
addr->bytelen = (family == AF_INET6 ? 16 : 4);
addr->bitlen = -1;
return 0;
}
if (strchr(name, ':'))
{
addr->family = AF_INET6;
if (family != AF_UNSPEC && family != AF_INET6)
return -1;
if (inet_pton(AF_INET6, name, addr->data) <= 0)
return -1;
addr->bytelen = 16;
addr->bitlen = -1;
return 0;
}
addr->family = AF_INET;
if (family != AF_UNSPEC && family != AF_INET)
return -1;
if (get_addr_ipv4((__u8 *)addr->data, name) <= 0)
return -1;
addr->bytelen = 4;
addr->bitlen = -1;
return 0;
}
int get_unsigned(unsigned *val, const char *arg, int base)
{
unsigned long res;
char *ptr;
if (!arg || !*arg)
return -1;
res = strtoul(arg, &ptr, base);
/* empty string or trailing non-digits */
if (!ptr || ptr == arg || *ptr)
return -1;
/* overflow */
if (res == ULONG_MAX && errno == ERANGE)
return -1;
/* out side range of unsigned */
if (res > UINT_MAX)
return -1;
*val = res;
return 0;
}
int mask2bits(__u32 netmask)
{
unsigned bits = 0;
__u32 mask = ntohl(netmask);
__u32 host = ~mask;
if ((host & (host + 1)) != 0)
{
return -1;
}
for (; mask; mask <<= 1)
{
++bits;
}
return bits;
}
int get_netmask(unsigned *val, const char *arg, int base)
{
inet_prefix addr;
if (!get_unsigned(val, arg, base))
return 0;
/* try coverting dotted quad to CIDR */
if (!get_addr_1(&addr, arg, AF_INET) && addr.family == AF_INET) {
int b = mask2bits(addr.data[0]);
if (b >= 0) {
*val = b;
return 0;
}
}
return -1;
}
int af_bit_len(int af)
{
switch (af)
{
case AF_INET6:
return 128;
case AF_INET:
return 32;
case AF_DECnet:
return 16;
case AF_IPX:
return 80;
}
return 0;
}
int get_prefix_1(inet_prefix *dst, char *arg, int family)
{
int err;
unsigned plen;
char *slash;
memset(dst, 0, sizeof(*dst));
if (strcmp(arg, "default") == 0 ||
strcmp(arg, "any") == 0 ||
strcmp(arg, "all") == 0)
{
dst->family = family;
dst->bytelen = 0;
dst->bitlen = 0;
return 0;
}
slash = strchr(arg, '/');
if (slash)
*slash = 0;
err = get_addr_1(dst, arg, family);
if (err == 0)
{
dst->bitlen = af_bit_len(dst->family);
if (slash)
{
if (get_netmask(&plen, slash+1, 0)
|| plen > dst->bitlen)
{
err = -1;
goto done;
}
dst->flags |= 0x1;
dst->bitlen = plen;
}
}
done:
if (slash)
*slash = '/';
return err;
}
int get_prefix(inet_prefix *dst, char *arg, int family)
{
if (family == AF_PACKET)
{
return -1;
}
if (get_prefix_1(dst, arg, family))
{
return -1;
}
return 0;
}
int default_scope(inet_prefix *lcl)
{
if (lcl->family == AF_INET)
{
if (lcl->bytelen >= 1 && *(__u8 *)&lcl->data == 127)
{
return RT_SCOPE_HOST;
}
}
return 0;
}
int matches(const char *cmd, const char *pattern)
{
int len = strlen(cmd);
if (len > strlen(pattern))
{
return -1;
}
return memcmp(pattern, cmd, len);
}
int ipaddr_modify(int cmd, int flags, int argc, char **argv)
{
struct
{
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[256];
} req;
char *d = NULL;
char *l = NULL;
char *lcl_arg = NULL;
char *valid_lftp = NULL;
char *perferred_lftp = NULL;
inet_prefix lcl;
inet_prefix peer;
int local_len = 0;
int perr_len = 0;
int any_len = 0;
int scoped = 0;
int ret = 0;
__u32 preferred_lft = INFINITY_LIFE_TIME;
__u32 valid_left = INFINITY_LIFE_TIME;
struct ifa_cacheinfo cinfo;
unsigned int ifa_flags = 0;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
req.n.nlmsg_type = cmd;
req.ifa.ifa_family = AF_INET;
while (argc > 0)
{
if (strcmp(*argv, "dev") == 0)
{
argv++;
if (--argc <= 0)
{
ret = -1;
break;
}
d = *argv;
}
else
{
lcl_arg = *argv;
get_prefix(&lcl, *argv, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC)
{
req.ifa.ifa_family = lcl.family;
}
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
local_len = lcl.bytelen;
}
argc--;
argv++;
}
if (ifa_flags <= 0xff)
{
req.ifa.ifa_flags = ifa_flags;
}
else
{
//addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags);
}
if (d == NULL)
{
return -1;
}
if (local_len)
{
if (cmd == RTM_DELADDR && lcl.family == AF_INET
&& !(lcl.flags & 0x01))
{
}
else
{
addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
}
}
if (req.ifa.ifa_prefixlen == 0)
{
req.ifa.ifa_prefixlen = lcl.bitlen;
}
if (!scoped && cmd != RTM_DELADDR)
{
req.ifa.ifa_scope = default_scope(&lcl);
}
if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0)
{
return -1;
}
if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
{
return -2;
}
return 0;
}
int do_ipaddr(int argc, char **argv)
{
if (matches(*argv, "add") == 0)
{
return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, argc - 1,
argv + 1);
}
if (matches(*argv, "delete") == 0)
{
return ipaddr_modify(RTM_DELADDR, 0, argc - 1, argv + 1);
}
if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0)
{
return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc - 1, argv + 1);
}
return -1;
}
int main(int argc, char **argv)
{
if (argc != 4)
{
printf("usage: %s add/del <ip> dev <interface>\n", argv[0]);
printf("eg : %s add 192.168.1.2/24 dev eth0\n", argv[0]);
printf(" %s del 192.168.1.2/24 dev eth0\n", argv[0]);
return 1;
}
if (rtnl_open(&rth, 0) < 0)
{
rtnl_close(&rth);
return 1;
}
int ret = do_ipaddr(argc - 1, ++argv);
printf("ret = %d\n", ret);
rtnl_close(&rth);
return 0;
}