blob: 65ce68f01bd854dc23b011ea3691bab5209bb6a6 [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "l2utils.h"
void get_l2_map(L2Map *l2map)
{
int s;
struct {
struct nlmsghdr hdr;
struct ndmsg msg;
} nlreq;
struct sockaddr_nl addr;
struct msghdr msg;
static uint8_t l2buf[256 * 1024];
struct iovec iov = {.iov_base = l2buf, .iov_len = sizeof(l2buf)};
struct nlmsghdr *nh;
struct ndmsg *ndm;
struct nlattr *tb[NDA_MAX+1];
int len;
int af[] = {AF_INET, AF_INET6};
unsigned int i;
for (i = 0; i < (sizeof(af) / sizeof(af[0])); ++i) {
if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
perror("socket AF_NETLINK");
exit(1);
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0;
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind AF_NETLINK");
exit(1);
}
memset(&nlreq, 0, sizeof(nlreq));
nlreq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(nlreq.msg));
nlreq.hdr.nlmsg_type = RTM_GETNEIGH;
nlreq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlreq.msg.ndm_family = af[i];
if (send(s, &nlreq, nlreq.hdr.nlmsg_len, 0) < 0) {
perror("send AF_NETLINK");
exit(1);
}
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_controllen = 0;
msg.msg_control = NULL;
msg.msg_flags = 0;
if ((len = recvmsg(s, &msg, 0)) <= 0) {
perror("recvmsg AL_NETLINK");
exit(1);
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "recvmsg AL_NETLINK MSG_TRUNC\n");
exit(1);
}
memset(tb, 0, sizeof(tb));
nh = (struct nlmsghdr *)l2buf;
while (nlmsg_ok(nh, len)) {
ndm = (struct ndmsg *)nlmsg_data(nh);
if (nlmsg_parse(nh, sizeof(*ndm), tb, NDA_MAX, NULL)) {
fprintf(stderr, "nlmsg_parse failed\n");
exit(1);
}
if (tb[NDA_DST] && tb[NDA_LLADDR] &&
!(ndm->ndm_state & (NUD_INCOMPLETE | NUD_FAILED)) &&
(ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)) {
char mac[18];
char ipaddr[INET6_ADDRSTRLEN];
uint8_t *p;
p = (uint8_t *)nla_data(tb[NDA_LLADDR]);
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
p[0], p[1], p[2], p[3], p[4], p[5]);
p = (uint8_t *)nla_data(tb[NDA_DST]);
inet_ntop(ndm->ndm_family, p, ipaddr, sizeof(ipaddr));
(*l2map)[std::string(ipaddr)] = std::string(mac);
}
nh = nlmsg_next(nh, &len);
}
close(s);
}
}
std::string get_l2addr_for_ip(std::string ipaddr)
{
static L2Map l2map;
std::string mac;
L2Map::const_iterator l2i = l2map.find(ipaddr);
if (l2i == l2map.end()) {
/* If we only just saw this IP address, it may not be present in
* the cached data we have. Refresh the cache and try again. */
get_l2_map(&l2map);
l2i = l2map.find(ipaddr);
}
if (l2i == l2map.end()) {
mac = std::string("00:00:00:00:00:00");
} else {
mac = l2i->second;
}
return mac;
}