【ip addr】实现代码

#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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡致云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值