2 * Copyright (c) 2011, 2012, 2013, 2014, 2017 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "route-table.h"
22 #include <sys/types.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <sys/socket.h>
26 #include <linux/rtnetlink.h>
32 #include "netlink-notifier.h"
33 #include "netlink-socket.h"
34 #include "openvswitch/ofpbuf.h"
35 #include "ovs-router.h"
37 #include "rtnetlink.h"
38 #include "tnl-ports.h"
39 #include "openvswitch/vlog.h"
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.) */
45 VLOG_DEFINE_THIS_MODULE(route_table
);
48 /* Copied from struct rtmsg. */
49 unsigned char rtm_dst_len
;
51 /* Extracted from Netlink attributes. */
52 struct in6_addr rta_dst
; /* 0 if missing. */
53 struct in6_addr rta_gw
;
54 char ifname
[IFNAMSIZ
]; /* Interface name. */
58 /* A digested version of a route message sent down by the kernel to indicate
59 * that a route has changed. */
60 struct route_table_msg
{
61 bool relevant
; /* Should this message be processed? */
62 int nlmsg_type
; /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */
63 struct route_data rd
; /* Data parsed from this message. */
66 static struct ovs_mutex route_table_mutex
= OVS_MUTEX_INITIALIZER
;
67 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(5, 20);
69 /* Global change number for route-table, which should be incremented
70 * every time route_table_reset() is called. */
71 static uint64_t rt_change_seq
;
73 static struct nln
*nln
= NULL
;
74 static struct route_table_msg rtmsg
;
75 static struct nln_notifier
*route_notifier
= NULL
;
76 static struct nln_notifier
*route6_notifier
= NULL
;
77 static struct nln_notifier
*name_notifier
= NULL
;
79 static bool route_table_valid
= false;
81 static int route_table_reset(void);
82 static void route_table_handle_msg(const struct route_table_msg
*);
83 static int route_table_parse(struct ofpbuf
*, struct route_table_msg
*);
84 static void route_table_change(const struct route_table_msg
*, void *);
85 static void route_map_clear(void);
87 static void name_table_init(void);
88 static void name_table_change(const struct rtnetlink_change
*, void *);
91 route_table_get_change_seq(void)
96 /* Users of the route_table module should register themselves with this
97 * function before making any other route_table function calls. */
99 route_table_init(void)
100 OVS_EXCLUDED(route_table_mutex
)
102 ovs_mutex_lock(&route_table_mutex
);
104 ovs_assert(!route_notifier
);
105 ovs_assert(!route6_notifier
);
108 nln
= nln_create(NETLINK_ROUTE
, (nln_parse_func
*) route_table_parse
,
112 nln_notifier_create(nln
, RTNLGRP_IPV4_ROUTE
,
113 (nln_notify_func
*) route_table_change
, NULL
);
115 nln_notifier_create(nln
, RTNLGRP_IPV6_ROUTE
,
116 (nln_notify_func
*) route_table_change
, NULL
);
121 ovs_mutex_unlock(&route_table_mutex
);
124 /* Run periodically to update the locally maintained routing table. */
126 route_table_run(void)
127 OVS_EXCLUDED(route_table_mutex
)
129 ovs_mutex_lock(&route_table_mutex
);
134 if (!route_table_valid
) {
138 ovs_mutex_unlock(&route_table_mutex
);
141 /* Causes poll_block() to wake up when route_table updates are required. */
143 route_table_wait(void)
144 OVS_EXCLUDED(route_table_mutex
)
146 ovs_mutex_lock(&route_table_mutex
);
151 ovs_mutex_unlock(&route_table_mutex
);
155 route_table_reset(void)
158 struct rtgenmsg
*rtgenmsg
;
159 uint64_t reply_stub
[NL_DUMP_BUFSIZE
/ 8];
160 struct ofpbuf request
, reply
, buf
;
163 netdev_get_addrs_list_flush();
164 route_table_valid
= true;
167 ofpbuf_init(&request
, 0);
169 nl_msg_put_nlmsghdr(&request
, sizeof *rtgenmsg
, RTM_GETROUTE
,
172 rtgenmsg
= ofpbuf_put_zeros(&request
, sizeof *rtgenmsg
);
173 rtgenmsg
->rtgen_family
= AF_UNSPEC
;
175 nl_dump_start(&dump
, NETLINK_ROUTE
, &request
);
176 ofpbuf_uninit(&request
);
178 ofpbuf_use_stub(&buf
, reply_stub
, sizeof reply_stub
);
179 while (nl_dump_next(&dump
, &reply
, &buf
)) {
180 struct route_table_msg msg
;
182 if (route_table_parse(&reply
, &msg
)) {
183 route_table_handle_msg(&msg
);
188 return nl_dump_done(&dump
);
191 /* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse
194 route_table_parse(struct ofpbuf
*buf
, struct route_table_msg
*change
)
196 bool parsed
, ipv4
= false;
198 static const struct nl_policy policy
[] = {
199 [RTA_DST
] = { .type
= NL_A_U32
, .optional
= true },
200 [RTA_OIF
] = { .type
= NL_A_U32
, .optional
= true },
201 [RTA_GATEWAY
] = { .type
= NL_A_U32
, .optional
= true },
202 [RTA_MARK
] = { .type
= NL_A_U32
, .optional
= true },
205 static const struct nl_policy policy6
[] = {
206 [RTA_DST
] = { .type
= NL_A_IPV6
, .optional
= true },
207 [RTA_OIF
] = { .type
= NL_A_U32
, .optional
= true },
208 [RTA_MARK
] = { .type
= NL_A_U32
, .optional
= true },
209 [RTA_GATEWAY
] = { .type
= NL_A_IPV6
, .optional
= true },
212 struct nlattr
*attrs
[ARRAY_SIZE(policy
)];
213 const struct rtmsg
*rtm
;
215 rtm
= ofpbuf_at(buf
, NLMSG_HDRLEN
, sizeof *rtm
);
217 if (rtm
->rtm_family
== AF_INET
) {
218 parsed
= nl_policy_parse(buf
, NLMSG_HDRLEN
+ sizeof(struct rtmsg
),
219 policy
, attrs
, ARRAY_SIZE(policy
));
221 } else if (rtm
->rtm_family
== AF_INET6
) {
222 parsed
= nl_policy_parse(buf
, NLMSG_HDRLEN
+ sizeof(struct rtmsg
),
223 policy6
, attrs
, ARRAY_SIZE(policy6
));
225 VLOG_DBG_RL(&rl
, "received non AF_INET rtnetlink route message");
230 const struct nlmsghdr
*nlmsg
;
231 int rta_oif
; /* Output interface index. */
235 memset(change
, 0, sizeof *change
);
236 change
->relevant
= true;
238 if (rtm
->rtm_scope
== RT_SCOPE_NOWHERE
) {
239 change
->relevant
= false;
242 if (rtm
->rtm_type
!= RTN_UNICAST
&&
243 rtm
->rtm_type
!= RTN_LOCAL
) {
244 change
->relevant
= false;
246 change
->nlmsg_type
= nlmsg
->nlmsg_type
;
247 change
->rd
.rtm_dst_len
= rtm
->rtm_dst_len
+ (ipv4
? 96 : 0);
248 if (attrs
[RTA_OIF
]) {
249 rta_oif
= nl_attr_get_u32(attrs
[RTA_OIF
]);
251 if (!if_indextoname(rta_oif
, change
->rd
.ifname
)) {
254 VLOG_DBG_RL(&rl
, "Could not find interface name[%u]: %s",
255 rta_oif
, ovs_strerror(error
));
256 if (error
== ENXIO
) {
257 change
->relevant
= false;
264 if (attrs
[RTA_DST
]) {
267 dst
= nl_attr_get_be32(attrs
[RTA_DST
]);
268 in6_addr_set_mapped_ipv4(&change
->rd
.rta_dst
, dst
);
270 change
->rd
.rta_dst
= nl_attr_get_in6_addr(attrs
[RTA_DST
]);
273 in6_addr_set_mapped_ipv4(&change
->rd
.rta_dst
, 0);
275 if (attrs
[RTA_GATEWAY
]) {
278 gw
= nl_attr_get_be32(attrs
[RTA_GATEWAY
]);
279 in6_addr_set_mapped_ipv4(&change
->rd
.rta_gw
, gw
);
281 change
->rd
.rta_gw
= nl_attr_get_in6_addr(attrs
[RTA_GATEWAY
]);
284 if (attrs
[RTA_MARK
]) {
285 change
->rd
.mark
= nl_attr_get_u32(attrs
[RTA_MARK
]);
288 VLOG_DBG_RL(&rl
, "received unparseable rtnetlink route message");
293 return ipv4
? RTNLGRP_IPV4_ROUTE
: RTNLGRP_IPV6_ROUTE
;
297 route_table_change(const struct route_table_msg
*change OVS_UNUSED
,
298 void *aux OVS_UNUSED
)
300 route_table_valid
= false;
304 route_table_handle_msg(const struct route_table_msg
*change
)
306 if (change
->relevant
&& change
->nlmsg_type
== RTM_NEWROUTE
) {
307 const struct route_data
*rd
= &change
->rd
;
309 ovs_router_insert(rd
->mark
, &rd
->rta_dst
, rd
->rtm_dst_len
,
310 rd
->ifname
, &rd
->rta_gw
);
315 route_map_clear(void)
321 route_table_fallback_lookup(const struct in6_addr
*ip6_dst OVS_UNUSED
,
322 char name
[] OVS_UNUSED
,
323 struct in6_addr
*gw6
)
333 name_table_init(void)
335 name_notifier
= rtnetlink_notifier_create(name_table_change
, NULL
);
340 name_table_change(const struct rtnetlink_change
*change
,
341 void *aux OVS_UNUSED
)
343 /* Changes to interface status can cause routing table changes that some
344 * versions of the linux kernel do not advertise for some reason. */
345 route_table_valid
= false;
347 if (change
&& change
->nlmsg_type
== RTM_DELLINK
) {
348 if (change
->ifname
) {
349 tnl_port_map_delete_ipdev(change
->ifname
);