|
@@ -1,473 +0,0 @@
|
|
|
-/* RTNETLINK client
|
|
|
- *
|
|
|
- * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
|
|
|
- * Written by David Howells (dhowells@redhat.com)
|
|
|
- *
|
|
|
- * This program is free software; you can redistribute it and/or
|
|
|
- * modify it under the terms of the GNU General Public License
|
|
|
- * as published by the Free Software Foundation; either version
|
|
|
- * 2 of the License, or (at your option) any later version.
|
|
|
- */
|
|
|
-#include <linux/netlink.h>
|
|
|
-#include <linux/rtnetlink.h>
|
|
|
-#include <linux/if_addr.h>
|
|
|
-#include <linux/if_arp.h>
|
|
|
-#include <linux/inetdevice.h>
|
|
|
-#include <net/netlink.h>
|
|
|
-#include "internal.h"
|
|
|
-
|
|
|
-struct afs_rtm_desc {
|
|
|
- struct socket *nlsock;
|
|
|
- struct afs_interface *bufs;
|
|
|
- u8 *mac;
|
|
|
- size_t nbufs;
|
|
|
- size_t maxbufs;
|
|
|
- void *data;
|
|
|
- ssize_t datalen;
|
|
|
- size_t datamax;
|
|
|
- int msg_seq;
|
|
|
- unsigned mac_index;
|
|
|
- bool wantloopback;
|
|
|
- int (*parse)(struct afs_rtm_desc *, struct nlmsghdr *);
|
|
|
-};
|
|
|
-
|
|
|
-/*
|
|
|
- * parse an RTM_GETADDR response
|
|
|
- */
|
|
|
-static int afs_rtm_getaddr_parse(struct afs_rtm_desc *desc,
|
|
|
- struct nlmsghdr *nlhdr)
|
|
|
-{
|
|
|
- struct afs_interface *this;
|
|
|
- struct ifaddrmsg *ifa;
|
|
|
- struct rtattr *rtattr;
|
|
|
- const char *name;
|
|
|
- size_t len;
|
|
|
-
|
|
|
- ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr);
|
|
|
-
|
|
|
- _enter("{ix=%d,af=%d}", ifa->ifa_index, ifa->ifa_family);
|
|
|
-
|
|
|
- if (ifa->ifa_family != AF_INET) {
|
|
|
- _leave(" = 0 [family %d]", ifa->ifa_family);
|
|
|
- return 0;
|
|
|
- }
|
|
|
- if (desc->nbufs >= desc->maxbufs) {
|
|
|
- _leave(" = 0 [max %zu/%zu]", desc->nbufs, desc->maxbufs);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- this = &desc->bufs[desc->nbufs];
|
|
|
-
|
|
|
- this->index = ifa->ifa_index;
|
|
|
- this->netmask.s_addr = inet_make_mask(ifa->ifa_prefixlen);
|
|
|
- this->mtu = 0;
|
|
|
-
|
|
|
- rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifaddrmsg));
|
|
|
- len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifaddrmsg));
|
|
|
-
|
|
|
- name = "unknown";
|
|
|
- for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
|
|
|
- switch (rtattr->rta_type) {
|
|
|
- case IFA_ADDRESS:
|
|
|
- memcpy(&this->address, RTA_DATA(rtattr), 4);
|
|
|
- break;
|
|
|
- case IFA_LABEL:
|
|
|
- name = RTA_DATA(rtattr);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT,
|
|
|
- name, NIPQUAD(this->address), NIPQUAD(this->netmask));
|
|
|
-
|
|
|
- desc->nbufs++;
|
|
|
- _leave(" = 0");
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * parse an RTM_GETLINK response for MTUs
|
|
|
- */
|
|
|
-static int afs_rtm_getlink_if_parse(struct afs_rtm_desc *desc,
|
|
|
- struct nlmsghdr *nlhdr)
|
|
|
-{
|
|
|
- struct afs_interface *this;
|
|
|
- struct ifinfomsg *ifi;
|
|
|
- struct rtattr *rtattr;
|
|
|
- const char *name;
|
|
|
- size_t len, loop;
|
|
|
-
|
|
|
- ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
|
|
|
-
|
|
|
- _enter("{ix=%d}", ifi->ifi_index);
|
|
|
-
|
|
|
- for (loop = 0; loop < desc->nbufs; loop++) {
|
|
|
- this = &desc->bufs[loop];
|
|
|
- if (this->index == ifi->ifi_index)
|
|
|
- goto found;
|
|
|
- }
|
|
|
-
|
|
|
- _leave(" = 0 [no match]");
|
|
|
- return 0;
|
|
|
-
|
|
|
-found:
|
|
|
- if (ifi->ifi_type == ARPHRD_LOOPBACK && !desc->wantloopback) {
|
|
|
- _leave(" = 0 [loopback]");
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
|
|
- len = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
|
|
|
-
|
|
|
- name = "unknown";
|
|
|
- for (; RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
|
|
|
- switch (rtattr->rta_type) {
|
|
|
- case IFLA_MTU:
|
|
|
- memcpy(&this->mtu, RTA_DATA(rtattr), 4);
|
|
|
- break;
|
|
|
- case IFLA_IFNAME:
|
|
|
- name = RTA_DATA(rtattr);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- _debug("%s: "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
|
|
|
- name, NIPQUAD(this->address), NIPQUAD(this->netmask),
|
|
|
- this->mtu);
|
|
|
-
|
|
|
- _leave(" = 0");
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * parse an RTM_GETLINK response for the MAC address belonging to the lowest
|
|
|
- * non-internal interface
|
|
|
- */
|
|
|
-static int afs_rtm_getlink_mac_parse(struct afs_rtm_desc *desc,
|
|
|
- struct nlmsghdr *nlhdr)
|
|
|
-{
|
|
|
- struct ifinfomsg *ifi;
|
|
|
- struct rtattr *rtattr;
|
|
|
- const char *name;
|
|
|
- size_t remain, len;
|
|
|
- bool set;
|
|
|
-
|
|
|
- ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr);
|
|
|
-
|
|
|
- _enter("{ix=%d}", ifi->ifi_index);
|
|
|
-
|
|
|
- if (ifi->ifi_index >= desc->mac_index) {
|
|
|
- _leave(" = 0 [high]");
|
|
|
- return 0;
|
|
|
- }
|
|
|
- if (ifi->ifi_type == ARPHRD_LOOPBACK) {
|
|
|
- _leave(" = 0 [loopback]");
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- rtattr = NLMSG_DATA(nlhdr) + NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
|
|
- remain = NLMSG_PAYLOAD(nlhdr, sizeof(struct ifinfomsg));
|
|
|
-
|
|
|
- name = "unknown";
|
|
|
- set = false;
|
|
|
- for (; RTA_OK(rtattr, remain); rtattr = RTA_NEXT(rtattr, remain)) {
|
|
|
- switch (rtattr->rta_type) {
|
|
|
- case IFLA_ADDRESS:
|
|
|
- len = RTA_PAYLOAD(rtattr);
|
|
|
- memcpy(desc->mac, RTA_DATA(rtattr),
|
|
|
- min_t(size_t, len, 6));
|
|
|
- desc->mac_index = ifi->ifi_index;
|
|
|
- set = true;
|
|
|
- break;
|
|
|
- case IFLA_IFNAME:
|
|
|
- name = RTA_DATA(rtattr);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (set)
|
|
|
- _debug("%s: %02x:%02x:%02x:%02x:%02x:%02x",
|
|
|
- name,
|
|
|
- desc->mac[0], desc->mac[1], desc->mac[2],
|
|
|
- desc->mac[3], desc->mac[4], desc->mac[5]);
|
|
|
-
|
|
|
- _leave(" = 0");
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * read the rtnetlink response and pass to parsing routine
|
|
|
- */
|
|
|
-static int afs_read_rtm(struct afs_rtm_desc *desc)
|
|
|
-{
|
|
|
- struct nlmsghdr *nlhdr, tmphdr;
|
|
|
- struct msghdr msg;
|
|
|
- struct kvec iov[1];
|
|
|
- void *data;
|
|
|
- bool last = false;
|
|
|
- int len, ret, remain;
|
|
|
-
|
|
|
- _enter("");
|
|
|
-
|
|
|
- do {
|
|
|
- /* first of all peek to see how big the packet is */
|
|
|
- memset(&msg, 0, sizeof(msg));
|
|
|
- iov[0].iov_base = &tmphdr;
|
|
|
- iov[0].iov_len = sizeof(tmphdr);
|
|
|
- len = kernel_recvmsg(desc->nlsock, &msg, iov, 1,
|
|
|
- sizeof(tmphdr), MSG_PEEK | MSG_TRUNC);
|
|
|
- if (len < 0) {
|
|
|
- _leave(" = %d [peek]", len);
|
|
|
- return len;
|
|
|
- }
|
|
|
- if (len == 0)
|
|
|
- continue;
|
|
|
- if (len < sizeof(tmphdr) || len < NLMSG_PAYLOAD(&tmphdr, 0)) {
|
|
|
- _leave(" = -EMSGSIZE");
|
|
|
- return -EMSGSIZE;
|
|
|
- }
|
|
|
-
|
|
|
- if (desc->datamax < len) {
|
|
|
- kfree(desc->data);
|
|
|
- desc->data = NULL;
|
|
|
- data = kmalloc(len, GFP_KERNEL);
|
|
|
- if (!data)
|
|
|
- return -ENOMEM;
|
|
|
- desc->data = data;
|
|
|
- }
|
|
|
- desc->datamax = len;
|
|
|
-
|
|
|
- /* read all the data from this packet */
|
|
|
- iov[0].iov_base = desc->data;
|
|
|
- iov[0].iov_len = desc->datamax;
|
|
|
- desc->datalen = kernel_recvmsg(desc->nlsock, &msg, iov, 1,
|
|
|
- desc->datamax, 0);
|
|
|
- if (desc->datalen < 0) {
|
|
|
- _leave(" = %zd [recv]", desc->datalen);
|
|
|
- return desc->datalen;
|
|
|
- }
|
|
|
-
|
|
|
- nlhdr = desc->data;
|
|
|
-
|
|
|
- /* check if the header is valid */
|
|
|
- if (!NLMSG_OK(nlhdr, desc->datalen) ||
|
|
|
- nlhdr->nlmsg_type == NLMSG_ERROR) {
|
|
|
- _leave(" = -EIO");
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- /* see if this is the last message */
|
|
|
- if (nlhdr->nlmsg_type == NLMSG_DONE ||
|
|
|
- !(nlhdr->nlmsg_flags & NLM_F_MULTI))
|
|
|
- last = true;
|
|
|
-
|
|
|
- /* parse the bits we got this time */
|
|
|
- nlmsg_for_each_msg(nlhdr, desc->data, desc->datalen, remain) {
|
|
|
- ret = desc->parse(desc, nlhdr);
|
|
|
- if (ret < 0) {
|
|
|
- _leave(" = %d [parse]", ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- } while (!last);
|
|
|
-
|
|
|
- _leave(" = 0");
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * list the interface bound addresses to get the address and netmask
|
|
|
- */
|
|
|
-static int afs_rtm_getaddr(struct afs_rtm_desc *desc)
|
|
|
-{
|
|
|
- struct msghdr msg;
|
|
|
- struct kvec iov[1];
|
|
|
- int ret;
|
|
|
-
|
|
|
- struct {
|
|
|
- struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
|
|
|
- struct ifaddrmsg addr_msg __attribute__((aligned(NLMSG_ALIGNTO)));
|
|
|
- } request;
|
|
|
-
|
|
|
- _enter("");
|
|
|
-
|
|
|
- memset(&request, 0, sizeof(request));
|
|
|
-
|
|
|
- request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
|
|
- request.nl_msg.nlmsg_type = RTM_GETADDR;
|
|
|
- request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
|
|
- request.nl_msg.nlmsg_seq = desc->msg_seq++;
|
|
|
- request.nl_msg.nlmsg_pid = 0;
|
|
|
-
|
|
|
- memset(&msg, 0, sizeof(msg));
|
|
|
- iov[0].iov_base = &request;
|
|
|
- iov[0].iov_len = sizeof(request);
|
|
|
-
|
|
|
- ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
|
|
|
- _leave(" = %d", ret);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * list the interface link statuses to get the MTUs
|
|
|
- */
|
|
|
-static int afs_rtm_getlink(struct afs_rtm_desc *desc)
|
|
|
-{
|
|
|
- struct msghdr msg;
|
|
|
- struct kvec iov[1];
|
|
|
- int ret;
|
|
|
-
|
|
|
- struct {
|
|
|
- struct nlmsghdr nl_msg __attribute__((aligned(NLMSG_ALIGNTO)));
|
|
|
- struct ifinfomsg link_msg __attribute__((aligned(NLMSG_ALIGNTO)));
|
|
|
- } request;
|
|
|
-
|
|
|
- _enter("");
|
|
|
-
|
|
|
- memset(&request, 0, sizeof(request));
|
|
|
-
|
|
|
- request.nl_msg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
|
|
- request.nl_msg.nlmsg_type = RTM_GETLINK;
|
|
|
- request.nl_msg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
|
|
- request.nl_msg.nlmsg_seq = desc->msg_seq++;
|
|
|
- request.nl_msg.nlmsg_pid = 0;
|
|
|
-
|
|
|
- memset(&msg, 0, sizeof(msg));
|
|
|
- iov[0].iov_base = &request;
|
|
|
- iov[0].iov_len = sizeof(request);
|
|
|
-
|
|
|
- ret = kernel_sendmsg(desc->nlsock, &msg, iov, 1, iov[0].iov_len);
|
|
|
- _leave(" = %d", ret);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * cull any interface records for which there isn't an MTU value
|
|
|
- */
|
|
|
-static void afs_cull_interfaces(struct afs_rtm_desc *desc)
|
|
|
-{
|
|
|
- struct afs_interface *bufs = desc->bufs;
|
|
|
- size_t nbufs = desc->nbufs;
|
|
|
- int loop, point = 0;
|
|
|
-
|
|
|
- _enter("{%zu}", nbufs);
|
|
|
-
|
|
|
- for (loop = 0; loop < nbufs; loop++) {
|
|
|
- if (desc->bufs[loop].mtu != 0) {
|
|
|
- if (loop != point) {
|
|
|
- ASSERTCMP(loop, >, point);
|
|
|
- bufs[point] = bufs[loop];
|
|
|
- }
|
|
|
- point++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- desc->nbufs = point;
|
|
|
- _leave(" [%zu/%zu]", desc->nbufs, nbufs);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * get a list of this system's interface IPv4 addresses, netmasks and MTUs
|
|
|
- * - returns the number of interface records in the buffer
|
|
|
- */
|
|
|
-int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
|
|
|
- bool wantloopback)
|
|
|
-{
|
|
|
- struct afs_rtm_desc desc;
|
|
|
- int ret, loop;
|
|
|
-
|
|
|
- _enter("");
|
|
|
-
|
|
|
- memset(&desc, 0, sizeof(desc));
|
|
|
- desc.bufs = bufs;
|
|
|
- desc.maxbufs = maxbufs;
|
|
|
- desc.wantloopback = wantloopback;
|
|
|
-
|
|
|
- ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE,
|
|
|
- &desc.nlsock);
|
|
|
- if (ret < 0) {
|
|
|
- _leave(" = %d [sock]", ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- /* issue RTM_GETADDR */
|
|
|
- desc.parse = afs_rtm_getaddr_parse;
|
|
|
- ret = afs_rtm_getaddr(&desc);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
- ret = afs_read_rtm(&desc);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
-
|
|
|
- /* issue RTM_GETLINK */
|
|
|
- desc.parse = afs_rtm_getlink_if_parse;
|
|
|
- ret = afs_rtm_getlink(&desc);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
- ret = afs_read_rtm(&desc);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
-
|
|
|
- afs_cull_interfaces(&desc);
|
|
|
- ret = desc.nbufs;
|
|
|
-
|
|
|
- for (loop = 0; loop < ret; loop++)
|
|
|
- _debug("[%d] "NIPQUAD_FMT"/"NIPQUAD_FMT" mtu %u",
|
|
|
- bufs[loop].index,
|
|
|
- NIPQUAD(bufs[loop].address),
|
|
|
- NIPQUAD(bufs[loop].netmask),
|
|
|
- bufs[loop].mtu);
|
|
|
-
|
|
|
-error:
|
|
|
- kfree(desc.data);
|
|
|
- sock_release(desc.nlsock);
|
|
|
- _leave(" = %d", ret);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * get a MAC address from a random ethernet interface that has a real one
|
|
|
- * - the buffer should be 6 bytes in size
|
|
|
- */
|
|
|
-int afs_get_MAC_address(u8 mac[6])
|
|
|
-{
|
|
|
- struct afs_rtm_desc desc;
|
|
|
- int ret;
|
|
|
-
|
|
|
- _enter("");
|
|
|
-
|
|
|
- memset(&desc, 0, sizeof(desc));
|
|
|
- desc.mac = mac;
|
|
|
- desc.mac_index = UINT_MAX;
|
|
|
-
|
|
|
- ret = sock_create_kern(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE,
|
|
|
- &desc.nlsock);
|
|
|
- if (ret < 0) {
|
|
|
- _leave(" = %d [sock]", ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- /* issue RTM_GETLINK */
|
|
|
- desc.parse = afs_rtm_getlink_mac_parse;
|
|
|
- ret = afs_rtm_getlink(&desc);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
- ret = afs_read_rtm(&desc);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
-
|
|
|
- if (desc.mac_index < UINT_MAX) {
|
|
|
- /* got a MAC address */
|
|
|
- _debug("[%d] %02x:%02x:%02x:%02x:%02x:%02x",
|
|
|
- desc.mac_index,
|
|
|
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
|
- } else {
|
|
|
- ret = -ENONET;
|
|
|
- }
|
|
|
-
|
|
|
-error:
|
|
|
- sock_release(desc.nlsock);
|
|
|
- _leave(" = %d", ret);
|
|
|
- return ret;
|
|
|
-}
|