VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/rtmon_linux.c@ 87710

Last change on this file since 87710 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 6.8 KB
Line 
1/* $Id: rtmon_linux.c 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * NAT Network - IPv6 default route monitor for Linux netlink.
4 */
5
6/*
7 * Copyright (C) 2013-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#define LOG_GROUP LOG_GROUP_NAT_SERVICE
20
21#include "proxy.h"
22
23#include <sys/types.h> /* must come before linux/netlink */
24#include <sys/socket.h>
25
26#include <asm/types.h>
27#include <linux/netlink.h>
28#include <linux/rtnetlink.h>
29
30#include <errno.h>
31#include <string.h>
32#include <unistd.h>
33
34
35static int rtmon_check_defaults(const void *buf, size_t len);
36
37
38/**
39 * Read IPv6 routing table - Linux rtnetlink version.
40 *
41 * XXX: TODO: To avoid re-reading the table we should subscribe to
42 * updates by binding a monitoring NETLINK_ROUTE socket to
43 * sockaddr_nl::nl_groups = RTMGRP_IPV6_ROUTE.
44 *
45 * But that will provide updates only. Documentation is scarce, but
46 * from what I've seen it seems that to get accurate routing info the
47 * monitoring socket needs to be created first, then full routing
48 * table requested (easier to do via spearate socket), then monitoring
49 * socket polled for input. The first update(s) of the monitoring
50 * socket may happen before full table is returned, so we can't just
51 * count the defaults, we need to keep track of their { oif, gw } to
52 * correctly ignore updates that are reported via monitoring socket,
53 * but that are already reflected in the full routing table returned
54 * in response to our request.
55 */
56int
57rtmon_get_defaults(void)
58{
59 int rtsock;
60 ssize_t nsent, ssize;
61 int ndefrts;
62
63 char *buf = NULL;
64 size_t bufsize;
65
66 struct {
67 struct nlmsghdr nh;
68 struct rtmsg rtm;
69 char attrbuf[512];
70 } rtreq;
71
72 memset(&rtreq, 0, sizeof(rtreq));
73 rtreq.nh.nlmsg_type = RTM_GETROUTE;
74 rtreq.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
75 rtreq.rtm.rtm_family = AF_INET6;
76 rtreq.rtm.rtm_table = RT_TABLE_MAIN;
77 rtreq.rtm.rtm_protocol = RTPROT_UNSPEC;
78
79 rtreq.nh.nlmsg_len = NLMSG_SPACE(sizeof(rtreq.rtm));
80
81 bufsize = 1024;
82 ssize = bufsize;
83 for (;;) {
84 char *newbuf;
85 int recverr;
86
87 newbuf = (char *)realloc(buf, ssize);
88 if (newbuf == NULL) {
89 DPRINTF0(("rtmon: failed to %sallocate buffer\n",
90 buf == NULL ? "" : "re"));
91 free(buf);
92 return -1;
93 }
94
95 buf = newbuf;
96 bufsize = ssize;
97
98 /* it's easier to reopen than to flush */
99 rtsock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
100 if (rtsock < 0) {
101 DPRINTF0(("rtmon: failed to create netlink socket: %s", strerror(errno)));
102 free(buf);
103 return -1;
104 }
105
106 nsent = send(rtsock, &rtreq, rtreq.nh.nlmsg_len, 0);
107 if (nsent < 0) {
108 DPRINTF0(("rtmon: RTM_GETROUTE failed: %s", strerror(errno)));
109 close (rtsock);
110 free(buf);
111 return -1;
112 }
113
114 ssize = recv(rtsock, buf, bufsize, MSG_TRUNC);
115 recverr = errno;
116 close (rtsock);
117
118 if (ssize < 0) {
119 DPRINTF(("rtmon: failed to read RTM_GETROUTE response: %s",
120 strerror(recverr)));
121 free(buf);
122 return -1;
123 }
124
125 if ((size_t)ssize <= bufsize) {
126 DPRINTF2(("rtmon: RTM_GETROUTE: %lu bytes\n",
127 (unsigned long)ssize));
128 break;
129 }
130
131 DPRINTF2(("rtmon: RTM_GETROUTE: truncated %lu to %lu bytes, retrying\n",
132 (unsigned long)ssize, (unsigned long)bufsize));
133 /* try again with larger buffer */
134 }
135
136 ndefrts = rtmon_check_defaults(buf, (size_t)ssize);
137 free(buf);
138
139 if (ndefrts == 0) {
140 DPRINTF(("rtmon: no IPv6 default routes found\n"));
141 }
142 else {
143 DPRINTF(("rtmon: %d IPv6 default route%s found\n",
144 ndefrts,
145 ndefrts == 1 || ndefrts == -1 ? "" : "s"));
146 }
147
148 return ndefrts;
149}
150
151
152/**
153 * Scan netlink message in the buffer for IPv6 default route changes.
154 */
155static int
156rtmon_check_defaults(const void *buf, size_t len)
157{
158 struct nlmsghdr *nh;
159 int dfltdiff = 0;
160
161 for (nh = (struct nlmsghdr *)buf;
162 NLMSG_OK(nh, len);
163 nh = NLMSG_NEXT(nh, len))
164 {
165 struct rtmsg *rtm;
166 struct rtattr *rta;
167 int attrlen;
168 int delta = 0;
169 const void *gwbuf;
170 size_t gwlen;
171 int oif;
172
173 DPRINTF2(("nlmsg seq %d type %d flags 0x%x\n",
174 nh->nlmsg_seq, nh->nlmsg_type, nh->nlmsg_flags));
175
176 if (nh->nlmsg_type == NLMSG_DONE) {
177 break;
178 }
179
180 if (nh->nlmsg_type == NLMSG_ERROR) {
181 struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh);
182 DPRINTF2(("> error %d\n", ne->error));
183 LWIP_UNUSED_ARG(ne);
184 break;
185 }
186
187 if (nh->nlmsg_type < RTM_BASE || RTM_MAX <= nh->nlmsg_type) {
188 /* shouldn't happen */
189 DPRINTF2(("> not an RTM message!\n"));
190 continue;
191 }
192
193
194 rtm = (struct rtmsg *)NLMSG_DATA(nh);
195 attrlen = RTM_PAYLOAD(nh);
196
197 if (nh->nlmsg_type == RTM_NEWROUTE) {
198 delta = +1;
199 }
200 else if (nh->nlmsg_type == RTM_DELROUTE) {
201 delta = -1;
202 }
203 else {
204 /* shouldn't happen */
205 continue;
206 }
207
208 /*
209 * Is this an IPv6 default route in the main table? (Local
210 * table always has ::/0 reject route, hence the last check).
211 */
212 if (rtm->rtm_family == AF_INET6 /* should always be true */
213 && rtm->rtm_dst_len == 0
214 && rtm->rtm_table == RT_TABLE_MAIN)
215 {
216 dfltdiff += delta;
217 }
218 else {
219 /* some other route change */
220 continue;
221 }
222
223
224 gwbuf = NULL;
225 gwlen = 0;
226 oif = -1;
227
228 for (rta = RTM_RTA(rtm);
229 RTA_OK(rta, attrlen);
230 rta = RTA_NEXT(rta, attrlen))
231 {
232 if (rta->rta_type == RTA_GATEWAY) {
233 gwbuf = RTA_DATA(rta);
234 gwlen = RTA_PAYLOAD(rta);
235 }
236 else if (rta->rta_type == RTA_OIF) {
237 /* assert RTA_PAYLOAD(rta) == 4 */
238 memcpy(&oif, RTA_DATA(rta), sizeof(oif));
239 }
240 }
241
242 /* XXX: TODO: note that { oif, gw } was added/removed */
243 LWIP_UNUSED_ARG(gwbuf);
244 LWIP_UNUSED_ARG(gwlen);
245 LWIP_UNUSED_ARG(oif);
246 }
247
248 return dfltdiff;
249}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette