#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <fnmatch.h>
#include <time.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/sockios.h>
#include <linux/net_namespace.h>
#include <linux/rtnetlink.h>
#define IDXMAP_SIZE 1024
#define NLMSG_ALIGNTO 4U
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
#define NLMSG_TAIL(nmsg) ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
#define rtnl_dump_filter(rth, filter, arg) rtnl_dump_filter_nc(rth, filter, arg, 0)
#define hlist_for_each(pos, head) for (pos = (head)->first; pos ; pos = pos->next)
#define offsetof(type, member) ((size_t) &((type *)0)->member)
#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
#define format_host_rta(af, rta) format_host(af, RTA_PAYLOAD(rta), RTA_DATA(rta))
typedef int (*rtnl_filter_t)(struct nlmsghdr *n, void *);
typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen);
typedef int (*nl_ext_ack_fn_t)(const char *errmsg, uint32_t off, const struct nlmsghdr *inner_nlh);
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_LISTEN_ALL_NSID 0x01
#define RTNL_HANDLE_F_SUPPRESS_NLERR 0x02
int flags;
};
enum output_type {
PRINT_FP = 1,
PRINT_JSON = 2,
PRINT_ANY = 4,
};
struct rtnl_dump_filter_arg {
rtnl_filter_t filter;
void *arg1;
__u16 nc_flags;
};
struct nlmsg_list {
struct nlmsg_list *next;
struct nlmsghdr h;
};
struct nlmsg_chain {
struct nlmsg_list *head;
struct nlmsg_list *tail;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
struct ll_cache {
struct hlist_node idx_hash;
struct hlist_node name_hash;
unsigned flags;
unsigned index;
unsigned short type;
char name[];
};
struct rtnl_handle rth = { .fd = -1 };
static struct hlist_head idx_head[IDXMAP_SIZE];
static struct hlist_head name_head[IDXMAP_SIZE];
static inline void hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
static inline const char *rta_getattr_str(const struct rtattr *rta)
{
return (const char *)RTA_DATA(rta);
}
static inline __u32 rta_getattr_u32(const struct rtattr *rta)
{
return *(__u32 *)RTA_DATA(rta);
}
unsigned namehash(const char *str)
{
unsigned hash = 5381;
while (*str)
hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */
return hash;
}
//error 这里发生了错误!!!
int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,int len, unsigned short flags)
{
unsigned short type;
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
type = rta->rta_type & ~flags;
if ((type <= max) && (!tb[type]))
tb[type] = rta;
rta = RTA_NEXT(rta, len);
}
if (len)
fprintf(stderr, "!!!Deficit %d, rta_len=%d\n",len, rta->rta_len);
return 0;
}
int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
return parse_rtattr_flags(tb, max, rta, len, 0);
}
static struct ll_cache *ll_get_by_index(unsigned index)
{
struct hlist_node *n;
unsigned h = index & (IDXMAP_SIZE - 1);
hlist_for_each(n, &idx_head[h]) {
struct ll_cache *im = container_of(n, struct ll_cache, idx_hash);
if (im->index == index)
return im;
}
return NULL;
}
int ll_remember_index(struct nlmsghdr *n, void *arg)
{
unsigned int h;
const char *ifname;
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct ll_cache *im;
struct rtattr *tb[IFLA_MAX+1];
if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
return 0;
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
return -1;
im = ll_get_by_index(ifi->ifi_index);
if (n->nlmsg_type == RTM_DELLINK) {
if (im) {
hlist_del(&im->name_hash);
hlist_del(&im->idx_hash);
free(im);
}
return 0;
}
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
ifname = rta_getattr_str(tb[IFLA_IFNAME]);
if (ifname == NULL)
return 0;
if (im) {
/* change to existing entry */
if (strcmp(im->name, ifname) != 0) {
hlist_del(&im->name_hash);
h = namehash(ifname) & (IDXMAP_SIZE - 1);
hlist_add_head(&im->name_hash, &name_head[h]);
}
im->flags = ifi->ifi_flags;
return 0;
}
im = malloc(sizeof(*im) + strlen(ifname) + 1);
if (im == NULL)
return 0;
im->index = ifi->ifi_index;
strcpy(im->name, ifname);
im->type = ifi->ifi_type;
im->flags = ifi->ifi_flags;
h = ifi->ifi_index & (IDXMAP_SIZE - 1);
hlist_add_head(&im->idx_hash, &idx_head[h]);
h = namehash(ifname) & (IDXMAP_SIZE - 1);
hlist_add_head(&im->name_hash, &name_head[h]);
return 0;
}
static int store_nlmsg(struct nlmsghdr *n, void *arg)
{
struct nlmsg_chain *lchain = (struct nlmsg_chain *)arg;
struct nlmsg_list *h;
h = malloc(n->nlmsg_len + sizeof(void *));
if (h == NULL)
return -1;
memcpy(&h->h, n, n->nlmsg_len);
h->next = NULL;
if (lchain->tail)
lchain->tail->next = h;
else
lchain->head = h;
lchain->tail = h;
ll_remember_index(n, NULL);
return 0;
}
int rtnl_addrdump_req(struct rtnl_handle *rth, int family)
{
struct {
struct nlmsghdr nlh;
struct ifaddrmsg ifm;
} req = {
.nlh.nlmsg_len = sizeof(req),
.nlh.nlmsg_type = RTM_GETADDR,
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
.nlh.nlmsg_seq = rth->dump = ++rth->seq,
.ifm.ifa_family = family,
};
return send(rth->fd, &req, sizeof(req), 0);
}
//问题1,rth->fd存在问题。
int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family,req_filter_fn_t filter_fn)
{
struct {
struct nlmsghdr nlh;
struct ifinfomsg ifm;
char buf[1024];
} req = {
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nlh.nlmsg_type = RTM_GETLINK,
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
.nlh.nlmsg_seq = rth->dump = ++rth->seq,
.ifm.ifi_family = family,
};
int err;
if (!filter_fn)
return -EINVAL;
err = filter_fn(&req.nlh, sizeof(req));
if (err)
return err;
return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
}
int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
{
return 0;
}
static int __rtnl_recvmsg(int fd, struct msghdr *msg, int flags)
{
int len;
do {
len = recvmsg(fd, msg, flags);
} while (len < 0 && (errno == EINTR || errno == EAGAIN));
if (len < 0) {
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
return -errno;
}
if (len == 0) {
fprintf(stderr, "EOF on netlink\n");
return -ENODATA;
}
return len;
}
static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
{
struct iovec *iov = msg->msg_iov;
char *buf;
int len;
iov->iov_base = NULL;
iov->iov_len = 0;
len = __rtnl_recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC);
if (len < 0)
return len;
buf = malloc(len);
if (!buf) {
fprintf(stderr, "malloc error: not enough buffer\n");
return -ENOMEM;
}
iov->iov_base = buf;
iov->iov_len = len;
len = __rtnl_recvmsg(fd, msg, 0);
if (len < 0) {
free(buf);
return len;
}
if (answer)
*answer = buf;
else
free(buf);
return len;
}
static int rtnl_dump_done(struct nlmsghdr *h)
{
int len = *(int *)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(int))) {
fprintf(stderr, "DONE truncated\n");
return -1;
}
if (len < 0) {
errno = -len;
switch (errno) {
case ENOENT:
case EOPNOTSUPP:
return -1;
case EMSGSIZE:
fprintf(stderr,
"Error: Buffer too small for object.\n");
break;
default:
perror("RTNETLINK answers");
}
return len;
}
/* check for any messages returned from kernel */
nl_dump_ext_ack(h, NULL);
return 0;
}
static void rtnl_dump_error(const struct rtnl_handle *rth,
struct nlmsghdr *h)
{
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
fprintf(stderr, "ERROR truncated\n");
} else {
const struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
errno = -err->error;
if (rth->proto == NETLINK_SOCK_DIAG &&
(errno == ENOENT ||
errno == EOPNOTSUPP))
return;
if (!(rth->flags & RTNL_HANDLE_F_SUPPRESS_NLERR))
perror("RTNETLINK answers");
}
}
static int rtnl_dump_filter_l(struct rtnl_handle *rth,const struct rtnl_dump_filter_arg *arg)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char *buf;
int dump_intr = 0;
while (1) {
int status;
const struct rtnl_dump_filter_arg *a;
int found_done = 0;
int msglen = 0;
status = rtnl_recvmsg(rth->fd, &msg, &buf);
if (status < 0)
return status;
if (rth->dump_fp)
fwrite(buf, 1, NLMSG_ALIGN(status), rth->dump_fp);
for (a = arg; a->filter; a++) {
struct nlmsghdr *h = (struct nlmsghdr *)buf;
msglen = status;
while (NLMSG_OK(h, msglen)) {
int err = 0;
h->nlmsg_flags &= ~a->nc_flags;
if (nladdr.nl_pid != 0 ||
h->nlmsg_pid != rth->local.nl_pid ||
h->nlmsg_seq != rth->dump)
goto skip_it;
if (h->nlmsg_flags & NLM_F_DUMP_INTR)
dump_intr = 1;
if (h->nlmsg_type == NLMSG_DONE) {
err = rtnl_dump_done(h);
if (err < 0) {
free(buf);
return -1;
}
found_done = 1;
break; /* process next filter */
}
if (h->nlmsg_type == NLMSG_ERROR) {
rtnl_dump_error(rth, h);
free(buf);
return -1;
}
if (!rth->dump_fp) {
err = a->filter(h, a->arg1);
if (err < 0) {
free(buf);
return err;
}
}
skip_it:
h = NLMSG_NEXT(h, msglen);
}
}
free(buf);
if (found_done) {
if (dump_intr)
fprintf(stderr,
"Dump was interrupted and may be inconsistent.\n");
return 0;
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (msglen) {
fprintf(stderr, "!!!Remnant of size %d\n", msglen);
exit(1);
}
}
}
int rtnl_dump_filter_nc(struct rtnl_handle *rth,
rtnl_filter_t filter,
void *arg1, __u16 nc_flags)
{
const struct rtnl_dump_filter_arg a[2] = {
{ .filter = filter, .arg1 = arg1, .nc_flags = nc_flags, },
{ .filter = NULL, .arg1 = NULL, .nc_flags = 0, },
};
return rtnl_dump_filter_l(rth, a);
}
int ip_linkaddr_list(int family, req_filter_fn_t filter_fn,struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
{
if (rtnl_linkdump_req_filter_fn(&rth, AF_UNSPEC, filter_fn) < 0)
{
}
if (rtnl_dump_filter(&rth, store_nlmsg, linfo) < 0)
{
}
if (ainfo)
{
if (rtnl_addrdump_req(&rth, family) < 0)
{
}
if (rtnl_dump_filter(&rth, store_nlmsg, ainfo) < 0)
{
}
}
return 0;
}
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) {
fprintf(stderr,"addattr_l ERROR: message exceeded bound of %d\n", maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
if (alen)
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));
}
static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen)
{
int err;
err = addattr32(nlh, reqlen, IFLA_EXT_MASK, RTEXT_FILTER_VF);
return 0;
}
void free_nlmsg_chain(struct nlmsg_chain *info)
{
struct nlmsg_list *l, *n;
for (l = info->head; l; l = n)
{
n = l->next;
free(l);
}
}
const char *ll_idx_n2a(unsigned int idx)
{
static char buf[IFNAMSIZ];
snprintf(buf, sizeof(buf), "if%u", idx);
return buf;
}
int check_ifname(const char *name)
{
/* These checks mimic kernel checks in dev_valid_name */
if (*name == '\0')
return -1;
if (strlen(name) >= IFNAMSIZ)
return -1;
while (*name) {
if (*name == '/' || isspace(*name))
return -1;
++name;
}
return 0;
}
const char *get_ifname_rta(int ifindex, const struct rtattr *rta)
{
const char *name;
if (rta) {
name = rta_getattr_str(rta);
} else {
fprintf(stderr,
"BUG: device with ifindex %d has nil ifname\n",
ifindex);
name = ll_idx_n2a(ifindex);
}
if (check_ifname(name))
return NULL;
return name;
}
int print_linkinfo(struct nlmsghdr *n, void *arg)
{
FILE *fp = (FILE *)arg;
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct rtattr *tb[IFLA_MAX + 1];
int len = n->nlmsg_len;
const char *name;
unsigned int m_flag = 0;
char b1[64];
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]);
int group = rta_getattr_u32(tb[IFLA_GROUP]);
//接口索引值
printf("%d: ", ifi->ifi_index);
//网络接口别名
printf("%s ", name);
fflush(fp);
}
int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions,int protocol)
{
socklen_t addr_len;
int sndbuf = 32768;
int one = 1;
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_RCVBUF");
return -1;
}
/* Older kernels may no support extended ACK reporting */
setsockopt(rth->fd, SOL_NETLINK, NETLINK_EXT_ACK,
&one, sizeof(one));
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 netlink 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 (addr_len != sizeof(rth->local)) {
fprintf(stderr, "Wrong address length %d\n", addr_len);
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 int subscriptions)
{
return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
const char *rt_addr_n2a_r(int af, int len,const void *addr, char *buf, int buflen)
{
switch (af) {
case AF_INET:
case AF_INET6:
return inet_ntop(af, addr, buf, buflen);
case AF_BRIDGE:
{
const union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} *sa = addr;
switch (sa->sa.sa_family) {
case AF_INET:
return inet_ntop(AF_INET, &sa->sin.sin_addr,buf, buflen);
case AF_INET6:
return inet_ntop(AF_INET6, &sa->sin6.sin6_addr,buf, buflen);
}
}
default:
return "???";
}
}
const char *format_host_r(int af, int len, const void *addr,char *buf, int buflen)
{
return rt_addr_n2a_r(af, len, addr, buf, buflen);
}
const char *format_host(int af, int len, const void *addr)
{
static char buf[256];
return format_host_r(af, len, addr, buf, 256);
}
int print_addrinfo(struct nlmsghdr *n, void *arg)
{
FILE *fp = arg;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
unsigned int ifa_flags;
struct rtattr *rta_tb[IFA_MAX + 1];
char b1[64];
if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
return 0;
len -= NLMSG_LENGTH(sizeof(*ifa));
if (len < 0)
{
return -1;
}
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
if (rta_tb[IFA_LOCAL])
{
printf("%s \n",format_host_rta(ifa->ifa_family,rta_tb[IFA_LOCAL]));
}
fflush(fp);
return 0;
}
static int print_selected_addrinfo(struct ifinfomsg *ifi,struct nlmsg_list *ainfo, FILE *fp)
{
for (; ainfo; ainfo = ainfo->next)
{
struct nlmsghdr *n = &ainfo->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
if (n->nlmsg_type != RTM_NEWADDR)
continue;
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifa)))
return -1;
if (ifa->ifa_index != ifi->ifi_index )
continue;
print_addrinfo(n, fp);
}
fflush(fp);
}
int main()
{
struct nlmsg_chain linfo = {NULL, NULL};
struct nlmsg_chain _ainfo = {NULL, NULL}, *ainfo = NULL;
struct nlmsg_list *l;
char *filter_dev = NULL;
int no_link = 0;
ainfo = &_ainfo;
rtnl_open(&rth, 0);
ip_linkaddr_list(AF_UNSPEC, iplink_filter_req, &linfo, ainfo);
for (l = linfo.head; l; l = l->next)
{
struct nlmsghdr *n = &l->h;
struct ifinfomsg *ifi = NLMSG_DATA(n);
int res = 0;
print_linkinfo(n, stdout); //这里有点问题,需进行排查.
print_selected_addrinfo(ifi, ainfo->head, stdout);
}
fflush(stdout);
if (ainfo)
free_nlmsg_chain(ainfo);
free_nlmsg_chain(&linfo);
return 0;
}