| /* |
| * 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; |
| } |