• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <errno.h>
18 #include <unistd.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <linux/netlink.h>
22 #include <linux/rtnetlink.h>
23 
24 #define LOG_TAG "Netd"
25 #include <cutils/log.h>
26 
27 #include "NetdConstants.h"
28 #include "NetlinkCommands.h"
29 
30 namespace android {
31 namespace net {
32 
openNetlinkSocket(int protocol)33 int openNetlinkSocket(int protocol) {
34     int sock = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, protocol);
35     if (sock == -1) {
36         return -errno;
37     }
38     if (connect(sock, reinterpret_cast<const sockaddr*>(&KERNEL_NLADDR),
39                 sizeof(KERNEL_NLADDR)) == -1) {
40         return -errno;
41     }
42     return sock;
43 }
44 
recvNetlinkAck(int sock)45 int recvNetlinkAck(int sock) {
46     struct {
47         nlmsghdr msg;
48         nlmsgerr err;
49     } response;
50 
51     int ret = recv(sock, &response, sizeof(response), 0);
52 
53     if (ret == -1) {
54         ret = -errno;
55         ALOGE("netlink recv failed (%s)", strerror(-ret));
56         return ret;
57     }
58 
59     if (ret != sizeof(response)) {
60         ALOGE("bad netlink response message size (%d != %zu)", ret, sizeof(response));
61         return -EBADMSG;
62     }
63 
64     return response.err.error;  // Netlink errors are negative errno.
65 }
66 
67 // Sends a netlink request and possibly expects an ack.
68 // |iov| is an array of struct iovec that contains the netlink message payload.
69 // The netlink header is generated by this function based on |action| and |flags|.
70 // Returns -errno if there was an error or if the kernel reported an error.
71 #ifdef __clang__
72 #if __has_feature(address_sanitizer)
73 __attribute__((optnone))
74 #endif
75 #endif
sendNetlinkRequest(uint16_t action,uint16_t flags,iovec * iov,int iovlen,const NetlinkDumpCallback * callback)76 WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
77                                           const NetlinkDumpCallback *callback) {
78     nlmsghdr nlmsg = {
79         .nlmsg_type = action,
80         .nlmsg_flags = flags,
81     };
82     iov[0].iov_base = &nlmsg;
83     iov[0].iov_len = sizeof(nlmsg);
84     for (int i = 0; i < iovlen; ++i) {
85         nlmsg.nlmsg_len += iov[i].iov_len;
86     }
87 
88     int sock = openNetlinkSocket(NETLINK_ROUTE);
89     if (sock < 0) {
90         return sock;
91     }
92 
93     int ret = 0;
94 
95     if (writev(sock, iov, iovlen) == -1) {
96         ret = -errno;
97         ALOGE("netlink socket connect/writev failed (%s)", strerror(-ret));
98         close(sock);
99         return ret;
100     }
101 
102     if (flags & NLM_F_ACK) {
103         ret = recvNetlinkAck(sock);
104     } else if ((flags & NLM_F_DUMP) && callback != nullptr) {
105         ret = processNetlinkDump(sock, *callback);
106     }
107 
108     close(sock);
109 
110     return ret;
111 }
112 
sendNetlinkRequest(uint16_t action,uint16_t flags,iovec * iov,int iovlen)113 int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen) {
114     return sendNetlinkRequest(action, flags, iov, iovlen, nullptr);
115 }
116 
processNetlinkDump(int sock,const NetlinkDumpCallback & callback)117 int processNetlinkDump(int sock, const NetlinkDumpCallback& callback) {
118     char buf[kNetlinkDumpBufferSize];
119 
120     ssize_t bytesread;
121     do {
122         bytesread = read(sock, buf, sizeof(buf));
123 
124         if (bytesread < 0) {
125             return -errno;
126         }
127 
128         uint32_t len = bytesread;
129         for (nlmsghdr *nlh = reinterpret_cast<nlmsghdr *>(buf);
130              NLMSG_OK(nlh, len);
131              nlh = NLMSG_NEXT(nlh, len)) {
132             switch (nlh->nlmsg_type) {
133               case NLMSG_DONE:
134                 return 0;
135               case NLMSG_ERROR: {
136                 nlmsgerr *err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(nlh));
137                 return err->error;
138               }
139               default:
140                 callback(nlh);
141             }
142         }
143     } while (bytesread > 0);
144 
145     return 0;
146 }
147 
rtNetlinkFlush(uint16_t getAction,uint16_t deleteAction,const char * what,const NetlinkDumpFilter & shouldDelete)148 WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
149                                      const char *what, const NetlinkDumpFilter& shouldDelete) {
150     // RTM_GETxxx is always RTM_DELxxx + 1, see <linux/rtnetlink.h>.
151     if (getAction != deleteAction + 1) {
152         ALOGE("Unknown flush type getAction=%d deleteAction=%d", getAction, deleteAction);
153         return -EINVAL;
154     }
155 
156     int writeSock = openNetlinkSocket(NETLINK_ROUTE);
157     if (writeSock < 0) {
158         return writeSock;
159     }
160 
161     NetlinkDumpCallback callback = [writeSock, deleteAction, shouldDelete, what] (nlmsghdr *nlh) {
162         if (!shouldDelete(nlh)) return;
163 
164         nlh->nlmsg_type = deleteAction;
165         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
166         if (write(writeSock, nlh, nlh->nlmsg_len) == -1) {
167             ALOGE("Error writing flush request: %s", strerror(errno));
168             return;
169         }
170 
171         int ret = recvNetlinkAck(writeSock);
172         // A flush works by dumping routes and deleting each route as it's returned, and it can
173         // fail if something else deletes the route between the dump and the delete. This can
174         // happen, for example, if an interface goes down while we're trying to flush its routes.
175         // So ignore ENOENT.
176         if (ret != 0 && ret != -ENOENT) {
177             ALOGW("Flushing %s: %s", what, strerror(-ret));
178         }
179     };
180 
181     int ret = 0;
182     for (const int family : { AF_INET, AF_INET6 }) {
183         // struct fib_rule_hdr and struct rtmsg are functionally identical.
184         rtmsg rule = {
185             .rtm_family = static_cast<uint8_t>(family),
186         };
187         iovec iov[] = {
188             { NULL,  0 },
189             { &rule, sizeof(rule) },
190         };
191         uint16_t flags = NETLINK_DUMP_FLAGS;
192 
193         if ((ret = sendNetlinkRequest(getAction, flags, iov, ARRAY_SIZE(iov), &callback)) != 0) {
194             break;
195         }
196     }
197 
198     close(writeSock);
199 
200     return ret;
201 }
202 
getRtmU32Attribute(const nlmsghdr * nlh,int attribute)203 uint32_t getRtmU32Attribute(const nlmsghdr *nlh, int attribute) {
204     uint32_t rta_len = RTM_PAYLOAD(nlh);
205     rtmsg *msg = reinterpret_cast<rtmsg *>(NLMSG_DATA(nlh));
206     rtattr *rta = reinterpret_cast<rtattr *> RTM_RTA(msg);
207     for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
208         if (rta->rta_type == attribute) {
209             return *(static_cast<uint32_t *>(RTA_DATA(rta)));
210         }
211     }
212     return 0;
213 }
214 
215 }  // namespace net
216 }  // namespace android
217