]>
Commit | Line | Data |
---|---|---|
a132aa96 | 1 | /* |
31c193e9 | 2 | * Copyright (c) 2011, 2012, 2013, 2014, 2017 Nicira, Inc. |
a132aa96 EJ |
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 <config.h> | |
18 | ||
19 | #include "route-table.h" | |
20 | ||
58718f40 | 21 | #include <errno.h> |
b2befd5b BP |
22 | #include <sys/types.h> |
23 | #include <netinet/in.h> | |
a132aa96 EJ |
24 | #include <arpa/inet.h> |
25 | #include <sys/socket.h> | |
26 | #include <linux/rtnetlink.h> | |
27 | #include <net/if.h> | |
28 | ||
29 | #include "hash.h" | |
c2a1ceed | 30 | #include "netdev.h" |
a132aa96 | 31 | #include "netlink.h" |
45c8d3a1 | 32 | #include "netlink-notifier.h" |
a132aa96 | 33 | #include "netlink-socket.h" |
64c96779 | 34 | #include "openvswitch/ofpbuf.h" |
d9b4ebc5 | 35 | #include "ovs-router.h" |
0b8da9ae | 36 | #include "packets.h" |
7e9dcc0f | 37 | #include "rtnetlink.h" |
1921d80c | 38 | #include "tnl-ports.h" |
e6211adc | 39 | #include "openvswitch/vlog.h" |
a132aa96 | 40 | |
31c193e9 BP |
41 | /* Linux 2.6.36 added RTA_MARK, so define it just in case we're building with |
42 | * old headers. (We can't test for it with #ifdef because it's an enum.) */ | |
43 | #define RTA_MARK 16 | |
44 | ||
a132aa96 EJ |
45 | VLOG_DEFINE_THIS_MODULE(route_table); |
46 | ||
47 | struct route_data { | |
48 | /* Copied from struct rtmsg. */ | |
49 | unsigned char rtm_dst_len; | |
8e4e4588 | 50 | bool local; |
a132aa96 EJ |
51 | |
52 | /* Extracted from Netlink attributes. */ | |
0b8da9ae TLSC |
53 | struct in6_addr rta_dst; /* 0 if missing. */ |
54 | struct in6_addr rta_gw; | |
58718f40 | 55 | char ifname[IFNAMSIZ]; /* Interface name. */ |
f36786ce | 56 | uint32_t mark; |
a132aa96 EJ |
57 | }; |
58 | ||
59 | /* A digested version of a route message sent down by the kernel to indicate | |
60 | * that a route has changed. */ | |
61 | struct route_table_msg { | |
db2dede4 | 62 | bool relevant; /* Should this message be processed? */ |
a132aa96 EJ |
63 | int nlmsg_type; /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */ |
64 | struct route_data rd; /* Data parsed from this message. */ | |
65 | }; | |
66 | ||
3c27dbe6 | 67 | static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER; |
a132aa96 EJ |
68 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); |
69 | ||
41ca1e0a AW |
70 | /* Global change number for route-table, which should be incremented |
71 | * every time route_table_reset() is called. */ | |
72 | static uint64_t rt_change_seq; | |
73 | ||
0a811051 | 74 | static struct nln *nln = NULL; |
a132aa96 | 75 | static struct route_table_msg rtmsg; |
2ee6545f | 76 | static struct nln_notifier *route_notifier = NULL; |
0b8da9ae | 77 | static struct nln_notifier *route6_notifier = NULL; |
2ee6545f | 78 | static struct nln_notifier *name_notifier = NULL; |
f0e167f0 EJ |
79 | |
80 | static bool route_table_valid = false; | |
a132aa96 EJ |
81 | |
82 | static int route_table_reset(void); | |
f0e167f0 | 83 | static void route_table_handle_msg(const struct route_table_msg *); |
77ee67e4 | 84 | static int route_table_parse(struct ofpbuf *, struct route_table_msg *); |
a132aa96 | 85 | static void route_table_change(const struct route_table_msg *, void *); |
a132aa96 | 86 | static void route_map_clear(void); |
a132aa96 | 87 | |
b46ccdf5 | 88 | static void name_table_init(void); |
7e9dcc0f | 89 | static void name_table_change(const struct rtnetlink_change *, void *); |
b46ccdf5 | 90 | |
41ca1e0a AW |
91 | uint64_t |
92 | route_table_get_change_seq(void) | |
93 | { | |
94 | return rt_change_seq; | |
95 | } | |
96 | ||
a132aa96 EJ |
97 | /* Users of the route_table module should register themselves with this |
98 | * function before making any other route_table function calls. */ | |
99 | void | |
b772066f | 100 | route_table_init(void) |
3c27dbe6 | 101 | OVS_EXCLUDED(route_table_mutex) |
a132aa96 | 102 | { |
3c27dbe6 | 103 | ovs_mutex_lock(&route_table_mutex); |
b772066f PS |
104 | ovs_assert(!nln); |
105 | ovs_assert(!route_notifier); | |
0b8da9ae | 106 | ovs_assert(!route6_notifier); |
a132aa96 | 107 | |
b772066f | 108 | ovs_router_init(); |
77ee67e4 JR |
109 | nln = nln_create(NETLINK_ROUTE, (nln_parse_func *) route_table_parse, |
110 | &rtmsg); | |
2ee6545f | 111 | |
b772066f | 112 | route_notifier = |
77ee67e4 JR |
113 | nln_notifier_create(nln, RTNLGRP_IPV4_ROUTE, |
114 | (nln_notify_func *) route_table_change, NULL); | |
0b8da9ae | 115 | route6_notifier = |
77ee67e4 JR |
116 | nln_notifier_create(nln, RTNLGRP_IPV6_ROUTE, |
117 | (nln_notify_func *) route_table_change, NULL); | |
a132aa96 | 118 | |
b772066f PS |
119 | route_table_reset(); |
120 | name_table_init(); | |
a132aa96 | 121 | |
3c27dbe6 | 122 | ovs_mutex_unlock(&route_table_mutex); |
a132aa96 EJ |
123 | } |
124 | ||
125 | /* Run periodically to update the locally maintained routing table. */ | |
126 | void | |
127 | route_table_run(void) | |
3c27dbe6 | 128 | OVS_EXCLUDED(route_table_mutex) |
a132aa96 | 129 | { |
3c27dbe6 | 130 | ovs_mutex_lock(&route_table_mutex); |
77ee67e4 | 131 | if (nln) { |
7e9dcc0f | 132 | rtnetlink_run(); |
77ee67e4 | 133 | nln_run(nln); |
41ca1e0a AW |
134 | |
135 | if (!route_table_valid) { | |
136 | route_table_reset(); | |
137 | } | |
a132aa96 | 138 | } |
3c27dbe6 | 139 | ovs_mutex_unlock(&route_table_mutex); |
a132aa96 EJ |
140 | } |
141 | ||
142 | /* Causes poll_block() to wake up when route_table updates are required. */ | |
143 | void | |
144 | route_table_wait(void) | |
3c27dbe6 | 145 | OVS_EXCLUDED(route_table_mutex) |
a132aa96 | 146 | { |
3c27dbe6 | 147 | ovs_mutex_lock(&route_table_mutex); |
77ee67e4 | 148 | if (nln) { |
7e9dcc0f | 149 | rtnetlink_wait(); |
77ee67e4 | 150 | nln_wait(nln); |
a132aa96 | 151 | } |
3c27dbe6 | 152 | ovs_mutex_unlock(&route_table_mutex); |
a132aa96 EJ |
153 | } |
154 | ||
155 | static int | |
156 | route_table_reset(void) | |
157 | { | |
a132aa96 | 158 | struct nl_dump dump; |
396d492c | 159 | struct rtgenmsg *rtgenmsg; |
d57695d7 JS |
160 | uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; |
161 | struct ofpbuf request, reply, buf; | |
a132aa96 EJ |
162 | |
163 | route_map_clear(); | |
c2a1ceed | 164 | netdev_get_addrs_list_flush(); |
f0e167f0 | 165 | route_table_valid = true; |
41ca1e0a | 166 | rt_change_seq++; |
a132aa96 | 167 | |
a132aa96 EJ |
168 | ofpbuf_init(&request, 0); |
169 | ||
396d492c JP |
170 | nl_msg_put_nlmsghdr(&request, sizeof *rtgenmsg, RTM_GETROUTE, |
171 | NLM_F_REQUEST); | |
a132aa96 | 172 | |
396d492c JP |
173 | rtgenmsg = ofpbuf_put_zeros(&request, sizeof *rtgenmsg); |
174 | rtgenmsg->rtgen_family = AF_UNSPEC; | |
a132aa96 | 175 | |
a88b4e04 | 176 | nl_dump_start(&dump, NETLINK_ROUTE, &request); |
896b3272 | 177 | ofpbuf_uninit(&request); |
a132aa96 | 178 | |
d57695d7 JS |
179 | ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); |
180 | while (nl_dump_next(&dump, &reply, &buf)) { | |
a132aa96 EJ |
181 | struct route_table_msg msg; |
182 | ||
183 | if (route_table_parse(&reply, &msg)) { | |
f0e167f0 | 184 | route_table_handle_msg(&msg); |
a132aa96 EJ |
185 | } |
186 | } | |
d57695d7 | 187 | ofpbuf_uninit(&buf); |
a132aa96 | 188 | |
a88b4e04 | 189 | return nl_dump_done(&dump); |
a132aa96 EJ |
190 | } |
191 | ||
77ee67e4 JR |
192 | /* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse |
193 | * error. */ | |
194 | static int | |
a132aa96 EJ |
195 | route_table_parse(struct ofpbuf *buf, struct route_table_msg *change) |
196 | { | |
0b8da9ae | 197 | bool parsed, ipv4 = false; |
a132aa96 EJ |
198 | |
199 | static const struct nl_policy policy[] = { | |
200 | [RTA_DST] = { .type = NL_A_U32, .optional = true }, | |
d7623379 | 201 | [RTA_OIF] = { .type = NL_A_U32, .optional = true }, |
e0cc58f9 | 202 | [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true }, |
f36786ce | 203 | [RTA_MARK] = { .type = NL_A_U32, .optional = true }, |
a132aa96 EJ |
204 | }; |
205 | ||
0b8da9ae TLSC |
206 | static const struct nl_policy policy6[] = { |
207 | [RTA_DST] = { .type = NL_A_IPV6, .optional = true }, | |
208 | [RTA_OIF] = { .type = NL_A_U32, .optional = true }, | |
f36786ce | 209 | [RTA_MARK] = { .type = NL_A_U32, .optional = true }, |
0b8da9ae TLSC |
210 | [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true }, |
211 | }; | |
212 | ||
37cd552e | 213 | struct nlattr *attrs[ARRAY_SIZE(policy)]; |
0b8da9ae TLSC |
214 | const struct rtmsg *rtm; |
215 | ||
216 | rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm); | |
a132aa96 | 217 | |
0b8da9ae TLSC |
218 | if (rtm->rtm_family == AF_INET) { |
219 | parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg), | |
220 | policy, attrs, ARRAY_SIZE(policy)); | |
221 | ipv4 = true; | |
222 | } else if (rtm->rtm_family == AF_INET6) { | |
223 | parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg), | |
224 | policy6, attrs, ARRAY_SIZE(policy6)); | |
225 | } else { | |
226 | VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message"); | |
77ee67e4 | 227 | return 0; |
0b8da9ae | 228 | } |
a132aa96 EJ |
229 | |
230 | if (parsed) { | |
a132aa96 | 231 | const struct nlmsghdr *nlmsg; |
58718f40 | 232 | int rta_oif; /* Output interface index. */ |
a132aa96 | 233 | |
6fd6ed71 | 234 | nlmsg = buf->data; |
a132aa96 EJ |
235 | |
236 | memset(change, 0, sizeof *change); | |
db2dede4 EJ |
237 | change->relevant = true; |
238 | ||
239 | if (rtm->rtm_scope == RT_SCOPE_NOWHERE) { | |
240 | change->relevant = false; | |
241 | } | |
242 | ||
243 | if (rtm->rtm_type != RTN_UNICAST && | |
244 | rtm->rtm_type != RTN_LOCAL) { | |
245 | change->relevant = false; | |
246 | } | |
a132aa96 | 247 | change->nlmsg_type = nlmsg->nlmsg_type; |
0b8da9ae | 248 | change->rd.rtm_dst_len = rtm->rtm_dst_len + (ipv4 ? 96 : 0); |
8e4e4588 | 249 | change->rd.local = rtm->rtm_type == RTN_LOCAL; |
0b8da9ae TLSC |
250 | if (attrs[RTA_OIF]) { |
251 | rta_oif = nl_attr_get_u32(attrs[RTA_OIF]); | |
58718f40 | 252 | |
0b8da9ae TLSC |
253 | if (!if_indextoname(rta_oif, change->rd.ifname)) { |
254 | int error = errno; | |
58718f40 | 255 | |
0b8da9ae TLSC |
256 | VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s", |
257 | rta_oif, ovs_strerror(error)); | |
5e0d63f7 TLSC |
258 | if (error == ENXIO) { |
259 | change->relevant = false; | |
260 | } else { | |
77ee67e4 | 261 | return 0; |
5e0d63f7 | 262 | } |
0b8da9ae | 263 | } |
58718f40 | 264 | } |
a132aa96 EJ |
265 | |
266 | if (attrs[RTA_DST]) { | |
0b8da9ae TLSC |
267 | if (ipv4) { |
268 | ovs_be32 dst; | |
269 | dst = nl_attr_get_be32(attrs[RTA_DST]); | |
270 | in6_addr_set_mapped_ipv4(&change->rd.rta_dst, dst); | |
271 | } else { | |
272 | change->rd.rta_dst = nl_attr_get_in6_addr(attrs[RTA_DST]); | |
273 | } | |
274 | } else if (ipv4) { | |
275 | in6_addr_set_mapped_ipv4(&change->rd.rta_dst, 0); | |
a132aa96 | 276 | } |
e0cc58f9 | 277 | if (attrs[RTA_GATEWAY]) { |
0b8da9ae TLSC |
278 | if (ipv4) { |
279 | ovs_be32 gw; | |
280 | gw = nl_attr_get_be32(attrs[RTA_GATEWAY]); | |
281 | in6_addr_set_mapped_ipv4(&change->rd.rta_gw, gw); | |
282 | } else { | |
283 | change->rd.rta_gw = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]); | |
284 | } | |
e0cc58f9 | 285 | } |
f36786ce PS |
286 | if (attrs[RTA_MARK]) { |
287 | change->rd.mark = nl_attr_get_u32(attrs[RTA_MARK]); | |
288 | } | |
a132aa96 EJ |
289 | } else { |
290 | VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message"); | |
77ee67e4 | 291 | return 0; |
a132aa96 EJ |
292 | } |
293 | ||
77ee67e4 JR |
294 | /* Success. */ |
295 | return ipv4 ? RTNLGRP_IPV4_ROUTE : RTNLGRP_IPV6_ROUTE; | |
a132aa96 EJ |
296 | } |
297 | ||
298 | static void | |
f0e167f0 EJ |
299 | route_table_change(const struct route_table_msg *change OVS_UNUSED, |
300 | void *aux OVS_UNUSED) | |
a132aa96 | 301 | { |
f0e167f0 EJ |
302 | route_table_valid = false; |
303 | } | |
304 | ||
305 | static void | |
306 | route_table_handle_msg(const struct route_table_msg *change) | |
307 | { | |
d9b4ebc5 PS |
308 | if (change->relevant && change->nlmsg_type == RTM_NEWROUTE) { |
309 | const struct route_data *rd = &change->rd; | |
a132aa96 | 310 | |
ed52ca57 | 311 | ovs_router_insert(rd->mark, &rd->rta_dst, rd->rtm_dst_len, |
8e4e4588 | 312 | rd->local, rd->ifname, &rd->rta_gw); |
a132aa96 EJ |
313 | } |
314 | } | |
315 | ||
a132aa96 EJ |
316 | static void |
317 | route_map_clear(void) | |
318 | { | |
d9b4ebc5 | 319 | ovs_router_flush(); |
a132aa96 EJ |
320 | } |
321 | ||
88ffdc93 | 322 | bool |
ec6c5379 PS |
323 | route_table_fallback_lookup(const struct in6_addr *ip6_dst OVS_UNUSED, |
324 | char name[] OVS_UNUSED, | |
325 | struct in6_addr *gw6) | |
88ffdc93 | 326 | { |
ec6c5379 | 327 | *gw6 = in6addr_any; |
88ffdc93 YT |
328 | return false; |
329 | } | |
330 | ||
b46ccdf5 EJ |
331 | \f |
332 | /* name_table . */ | |
333 | ||
334 | static void | |
335 | name_table_init(void) | |
336 | { | |
7e9dcc0f | 337 | name_notifier = rtnetlink_notifier_create(name_table_change, NULL); |
b46ccdf5 EJ |
338 | } |
339 | ||
b46ccdf5 EJ |
340 | |
341 | static void | |
1921d80c | 342 | name_table_change(const struct rtnetlink_change *change, |
b46ccdf5 EJ |
343 | void *aux OVS_UNUSED) |
344 | { | |
345 | /* Changes to interface status can cause routing table changes that some | |
346 | * versions of the linux kernel do not advertise for some reason. */ | |
347 | route_table_valid = false; | |
1921d80c KF |
348 | |
349 | if (change && change->nlmsg_type == RTM_DELLINK) { | |
350 | if (change->ifname) { | |
351 | tnl_port_map_delete_ipdev(change->ifname); | |
352 | } | |
353 | } | |
b46ccdf5 | 354 | } |