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
;
52 /* Extracted from Netlink attributes. */
53 struct in6_addr rta_dst
; /* 0 if missing. */
54 struct in6_addr rta_gw
;
55 char ifname
[IFNAMSIZ
]; /* Interface name. */
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
{
62 bool relevant
; /* Should this message be processed? */
63 int nlmsg_type
; /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */
64 struct route_data rd
; /* Data parsed from this message. */
67 static struct ovs_mutex route_table_mutex
= OVS_MUTEX_INITIALIZER
;
68 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(5, 20);
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
;
74 static struct nln
*nln
= NULL
;
75 static struct route_table_msg rtmsg
;
76 static struct nln_notifier
*route_notifier
= NULL
;
77 static struct nln_notifier
*route6_notifier
= NULL
;
78 static struct nln_notifier
*name_notifier
= NULL
;
80 static bool route_table_valid
= false;
82 static int route_table_reset(void);
83 static void route_table_handle_msg(const struct route_table_msg
*);
84 static int route_table_parse(struct ofpbuf
*, struct route_table_msg
*);
85 static void route_table_change(const struct route_table_msg
*, void *);
86 static void route_map_clear(void);
88 static void name_table_init(void);
89 static void name_table_change(const struct rtnetlink_change
*, void *);
92 route_table_get_change_seq(void)
97 /* Users of the route_table module should register themselves with this
98 * function before making any other route_table function calls. */
100 route_table_init(void)
101 OVS_EXCLUDED(route_table_mutex
)
103 ovs_mutex_lock(&route_table_mutex
);
105 ovs_assert(!route_notifier
);
106 ovs_assert(!route6_notifier
);
109 nln
= nln_create(NETLINK_ROUTE
, (nln_parse_func
*) route_table_parse
,
113 nln_notifier_create(nln
, RTNLGRP_IPV4_ROUTE
,
114 (nln_notify_func
*) route_table_change
, NULL
);
116 nln_notifier_create(nln
, RTNLGRP_IPV6_ROUTE
,
117 (nln_notify_func
*) route_table_change
, NULL
);
122 ovs_mutex_unlock(&route_table_mutex
);
125 /* Run periodically to update the locally maintained routing table. */
127 route_table_run(void)
128 OVS_EXCLUDED(route_table_mutex
)
130 ovs_mutex_lock(&route_table_mutex
);
135 if (!route_table_valid
) {
139 ovs_mutex_unlock(&route_table_mutex
);
142 /* Causes poll_block() to wake up when route_table updates are required. */
144 route_table_wait(void)
145 OVS_EXCLUDED(route_table_mutex
)
147 ovs_mutex_lock(&route_table_mutex
);
152 ovs_mutex_unlock(&route_table_mutex
);
156 route_table_reset(void)
159 struct rtgenmsg
*rtgenmsg
;
160 uint64_t reply_stub
[NL_DUMP_BUFSIZE
/ 8];
161 struct ofpbuf request
, reply
, buf
;
164 netdev_get_addrs_list_flush();
165 route_table_valid
= true;
168 ofpbuf_init(&request
, 0);
170 nl_msg_put_nlmsghdr(&request
, sizeof *rtgenmsg
, RTM_GETROUTE
,
173 rtgenmsg
= ofpbuf_put_zeros(&request
, sizeof *rtgenmsg
);
174 rtgenmsg
->rtgen_family
= AF_UNSPEC
;
176 nl_dump_start(&dump
, NETLINK_ROUTE
, &request
);
177 ofpbuf_uninit(&request
);
179 ofpbuf_use_stub(&buf
, reply_stub
, sizeof reply_stub
);
180 while (nl_dump_next(&dump
, &reply
, &buf
)) {
181 struct route_table_msg msg
;
183 if (route_table_parse(&reply
, &msg
)) {
184 route_table_handle_msg(&msg
);
189 return nl_dump_done(&dump
);
192 /* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse
195 route_table_parse(struct ofpbuf
*buf
, struct route_table_msg
*change
)
197 bool parsed
, ipv4
= false;
199 static const struct nl_policy policy
[] = {
200 [RTA_DST
] = { .type
= NL_A_U32
, .optional
= true },
201 [RTA_OIF
] = { .type
= NL_A_U32
, .optional
= true },
202 [RTA_GATEWAY
] = { .type
= NL_A_U32
, .optional
= true },
203 [RTA_MARK
] = { .type
= NL_A_U32
, .optional
= true },
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 },
209 [RTA_MARK
] = { .type
= NL_A_U32
, .optional
= true },
210 [RTA_GATEWAY
] = { .type
= NL_A_IPV6
, .optional
= true },
213 struct nlattr
*attrs
[ARRAY_SIZE(policy
)];
214 const struct rtmsg
*rtm
;
216 rtm
= ofpbuf_at(buf
, NLMSG_HDRLEN
, sizeof *rtm
);
218 if (rtm
->rtm_family
== AF_INET
) {
219 parsed
= nl_policy_parse(buf
, NLMSG_HDRLEN
+ sizeof(struct rtmsg
),
220 policy
, attrs
, ARRAY_SIZE(policy
));
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
));
226 VLOG_DBG_RL(&rl
, "received non AF_INET rtnetlink route message");
231 const struct nlmsghdr
*nlmsg
;
232 int rta_oif
; /* Output interface index. */
236 memset(change
, 0, sizeof *change
);
237 change
->relevant
= true;
239 if (rtm
->rtm_scope
== RT_SCOPE_NOWHERE
) {
240 change
->relevant
= false;
243 if (rtm
->rtm_type
!= RTN_UNICAST
&&
244 rtm
->rtm_type
!= RTN_LOCAL
) {
245 change
->relevant
= false;
247 change
->nlmsg_type
= nlmsg
->nlmsg_type
;
248 change
->rd
.rtm_dst_len
= rtm
->rtm_dst_len
+ (ipv4
? 96 : 0);
249 change
->rd
.local
= rtm
->rtm_type
== RTN_LOCAL
;
250 if (attrs
[RTA_OIF
]) {
251 rta_oif
= nl_attr_get_u32(attrs
[RTA_OIF
]);
253 if (!if_indextoname(rta_oif
, change
->rd
.ifname
)) {
256 VLOG_DBG_RL(&rl
, "Could not find interface name[%u]: %s",
257 rta_oif
, ovs_strerror(error
));
258 if (error
== ENXIO
) {
259 change
->relevant
= false;
266 if (attrs
[RTA_DST
]) {
269 dst
= nl_attr_get_be32(attrs
[RTA_DST
]);
270 in6_addr_set_mapped_ipv4(&change
->rd
.rta_dst
, dst
);
272 change
->rd
.rta_dst
= nl_attr_get_in6_addr(attrs
[RTA_DST
]);
275 in6_addr_set_mapped_ipv4(&change
->rd
.rta_dst
, 0);
277 if (attrs
[RTA_GATEWAY
]) {
280 gw
= nl_attr_get_be32(attrs
[RTA_GATEWAY
]);
281 in6_addr_set_mapped_ipv4(&change
->rd
.rta_gw
, gw
);
283 change
->rd
.rta_gw
= nl_attr_get_in6_addr(attrs
[RTA_GATEWAY
]);
286 if (attrs
[RTA_MARK
]) {
287 change
->rd
.mark
= nl_attr_get_u32(attrs
[RTA_MARK
]);
290 VLOG_DBG_RL(&rl
, "received unparseable rtnetlink route message");
295 return ipv4
? RTNLGRP_IPV4_ROUTE
: RTNLGRP_IPV6_ROUTE
;
299 route_table_change(const struct route_table_msg
*change OVS_UNUSED
,
300 void *aux OVS_UNUSED
)
302 route_table_valid
= false;
306 route_table_handle_msg(const struct route_table_msg
*change
)
308 if (change
->relevant
&& change
->nlmsg_type
== RTM_NEWROUTE
) {
309 const struct route_data
*rd
= &change
->rd
;
311 ovs_router_insert(rd
->mark
, &rd
->rta_dst
, rd
->rtm_dst_len
,
312 rd
->local
, rd
->ifname
, &rd
->rta_gw
);
317 route_map_clear(void)
323 route_table_fallback_lookup(const struct in6_addr
*ip6_dst OVS_UNUSED
,
324 char name
[] OVS_UNUSED
,
325 struct in6_addr
*gw6
)
335 name_table_init(void)
337 name_notifier
= rtnetlink_notifier_create(name_table_change
, NULL
);
342 name_table_change(const struct rtnetlink_change
*change
,
343 void *aux OVS_UNUSED
)
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;
349 if (change
&& change
->nlmsg_type
== RTM_DELLINK
) {
350 if (change
->ifname
) {
351 tnl_port_map_delete_ipdev(change
->ifname
);