blob: be505c92bda2b6f2a43b490fe9cdc95ec1c46451 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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 "router.h"
#include <linux/rtnetlink.h>
#include <stddef.h>
#include <string.h>
#include "address.h"
#include "log.h"
template<class Request>
static void addRouterAttribute(Request& r,
int type,
const void* data,
size_t size) {
// Calculate the offset into the character buffer where the RTA data lives
// We use offsetof on the buffer to get it. This avoids undefined behavior
// by casting the buffer (which is safe because it's char) instead of the
// Request struct.(which is undefined because of aliasing)
size_t offset = NLMSG_ALIGN(r.hdr.nlmsg_len) - offsetof(Request, buf);
auto attr = reinterpret_cast<struct rtattr*>(r.buf + offset);
attr->rta_type = type;
attr->rta_len = RTA_LENGTH(size);
memcpy(RTA_DATA(attr), data, size);
// Update the message length to include the router attribute.
r.hdr.nlmsg_len = NLMSG_ALIGN(r.hdr.nlmsg_len) + RTA_ALIGN(attr->rta_len);
}
bool Router::init() {
// Create a netlink socket to the router
Result res = mSocket.open(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (!res) {
loge("Unable to open netlink socket: %s\n", res.c_str());
return false;
}
return true;
}
bool Router::addNeighbor(const struct in6_addr& address,
unsigned int interfaceIndex) {
struct Request {
struct nlmsghdr hdr;
struct ndmsg msg;
char buf[256];
} request;
memset(&request, 0, sizeof(request));
unsigned short msgLen = NLMSG_LENGTH(sizeof(request.msg));
// Set up a request to create a new neighbor
request.hdr.nlmsg_len = msgLen;
request.hdr.nlmsg_type = RTM_NEWNEIGH;
request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
// The neighbor is a permanent IPv6 proxy
request.msg.ndm_family = AF_INET6;
request.msg.ndm_state = NUD_PERMANENT;
request.msg.ndm_flags = NTF_PROXY;
request.msg.ndm_ifindex = interfaceIndex;
addRouterAttribute(request, NDA_DST, &address, sizeof(address));
return sendNetlinkMessage(&request, request.hdr.nlmsg_len);
}
bool Router::addRoute(const struct in6_addr& address,
uint8_t bits,
uint32_t ifaceIndex) {
struct Request {
struct nlmsghdr hdr;
struct rtmsg msg;
char buf[256];
} request;
memset(&request, 0, sizeof(request));
// Set up a request to create a new route
request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
request.hdr.nlmsg_type = RTM_NEWROUTE;
request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
request.msg.rtm_family = AF_INET6;
request.msg.rtm_dst_len = bits;
request.msg.rtm_table = RT_TABLE_MAIN;
request.msg.rtm_protocol = RTPROT_RA;
request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
request.msg.rtm_type = RTN_UNICAST;
addRouterAttribute(request, RTA_DST, &address, sizeof(address));
addRouterAttribute(request, RTA_OIF, &ifaceIndex, sizeof(ifaceIndex));
return sendNetlinkMessage(&request, request.hdr.nlmsg_len);
}
bool Router::setDefaultGateway(const struct in6_addr& address,
unsigned int ifaceIndex) {
struct Request {
struct nlmsghdr hdr;
struct rtmsg msg;
char buf[256];
} request;
memset(&request, 0, sizeof(request));
// Set up a request to create a new route
request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
request.hdr.nlmsg_type = RTM_NEWROUTE;
request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
request.msg.rtm_family = AF_INET6;
request.msg.rtm_dst_len = 0;
request.msg.rtm_src_len = 0;
request.msg.rtm_table = RT_TABLE_MAIN;
request.msg.rtm_protocol = RTPROT_RA;
request.msg.rtm_scope = RT_SCOPE_UNIVERSE;
request.msg.rtm_type = RTN_UNICAST;
struct in6_addr anyAddress;
memset(&anyAddress, 0, sizeof(anyAddress));
addRouterAttribute(request, RTA_GATEWAY, &address, sizeof(address));
addRouterAttribute(request, RTA_OIF, &ifaceIndex, sizeof(ifaceIndex));
addRouterAttribute(request, RTA_SRC, &anyAddress, sizeof(anyAddress));
return sendNetlinkMessage(&request, request.hdr.nlmsg_len);
}
bool Router::sendNetlinkMessage(const void* data, size_t size) {
struct sockaddr_nl netlinkAddress;
memset(&netlinkAddress, 0, sizeof(netlinkAddress));
netlinkAddress.nl_family = AF_NETLINK;
Result res = mSocket.sendTo(netlinkAddress, data, size);
if (!res) {
loge("Unable to send on netlink socket: %s\n", res.c_str());
return false;
}
return true;
}