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
*rtmsg
;
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 *rtmsg
, RTM_GETROUTE
, NLM_F_REQUEST
);
171 rtmsg
= ofpbuf_put_zeros(&request
, sizeof *rtmsg
);
172 rtmsg
->rtgen_family
= AF_UNSPEC
;
174 nl_dump_start(&dump
, NETLINK_ROUTE
, &request
);
175 ofpbuf_uninit(&request
);
177 ofpbuf_use_stub(&buf
, reply_stub
, sizeof reply_stub
);
178 while (nl_dump_next(&dump
, &reply
, &buf
)) {
179 struct route_table_msg msg
;
181 if (route_table_parse(&reply
, &msg
)) {
182 route_table_handle_msg(&msg
);
187 return nl_dump_done(&dump
);
190 /* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse
193 route_table_parse(struct ofpbuf
*buf
, struct route_table_msg
*change
)
195 bool parsed
, ipv4
= false;
197 static const struct nl_policy policy
[] = {
198 [RTA_DST
] = { .type
= NL_A_U32
, .optional
= true },
199 [RTA_OIF
] = { .type
= NL_A_U32
, .optional
= true },
200 [RTA_GATEWAY
] = { .type
= NL_A_U32
, .optional
= true },
201 [RTA_MARK
] = { .type
= NL_A_U32
, .optional
= true },
204 static const struct nl_policy policy6
[] = {
205 [RTA_DST
] = { .type
= NL_A_IPV6
, .optional
= true },
206 [RTA_OIF
] = { .type
= NL_A_U32
, .optional
= true },
207 [RTA_MARK
] = { .type
= NL_A_U32
, .optional
= true },
208 [RTA_GATEWAY
] = { .type
= NL_A_IPV6
, .optional
= true },
211 struct nlattr
*attrs
[ARRAY_SIZE(policy
)];
212 const struct rtmsg
*rtm
;
214 rtm
= ofpbuf_at(buf
, NLMSG_HDRLEN
, sizeof *rtm
);
216 if (rtm
->rtm_family
== AF_INET
) {
217 parsed
= nl_policy_parse(buf
, NLMSG_HDRLEN
+ sizeof(struct rtmsg
),
218 policy
, attrs
, ARRAY_SIZE(policy
));
220 } else if (rtm
->rtm_family
== AF_INET6
) {
221 parsed
= nl_policy_parse(buf
, NLMSG_HDRLEN
+ sizeof(struct rtmsg
),
222 policy6
, attrs
, ARRAY_SIZE(policy6
));
224 VLOG_DBG_RL(&rl
, "received non AF_INET rtnetlink route message");
229 const struct nlmsghdr
*nlmsg
;
230 int rta_oif
; /* Output interface index. */
234 memset(change
, 0, sizeof *change
);
235 change
->relevant
= true;
237 if (rtm
->rtm_scope
== RT_SCOPE_NOWHERE
) {
238 change
->relevant
= false;
241 if (rtm
->rtm_type
!= RTN_UNICAST
&&
242 rtm
->rtm_type
!= RTN_LOCAL
) {
243 change
->relevant
= false;
245 change
->nlmsg_type
= nlmsg
->nlmsg_type
;
246 change
->rd
.rtm_dst_len
= rtm
->rtm_dst_len
+ (ipv4
? 96 : 0);
247 if (attrs
[RTA_OIF
]) {
248 rta_oif
= nl_attr_get_u32(attrs
[RTA_OIF
]);
250 if (!if_indextoname(rta_oif
, change
->rd
.ifname
)) {
253 VLOG_DBG_RL(&rl
, "Could not find interface name[%u]: %s",
254 rta_oif
, ovs_strerror(error
));
255 if (error
== ENXIO
) {
256 change
->relevant
= false;
263 if (attrs
[RTA_DST
]) {
266 dst
= nl_attr_get_be32(attrs
[RTA_DST
]);
267 in6_addr_set_mapped_ipv4(&change
->rd
.rta_dst
, dst
);
269 change
->rd
.rta_dst
= nl_attr_get_in6_addr(attrs
[RTA_DST
]);
272 in6_addr_set_mapped_ipv4(&change
->rd
.rta_dst
, 0);
274 if (attrs
[RTA_GATEWAY
]) {
277 gw
= nl_attr_get_be32(attrs
[RTA_GATEWAY
]);
278 in6_addr_set_mapped_ipv4(&change
->rd
.rta_gw
, gw
);
280 change
->rd
.rta_gw
= nl_attr_get_in6_addr(attrs
[RTA_GATEWAY
]);
283 if (attrs
[RTA_MARK
]) {
284 change
->rd
.mark
= nl_attr_get_u32(attrs
[RTA_MARK
]);
287 VLOG_DBG_RL(&rl
, "received unparseable rtnetlink route message");
292 return ipv4
? RTNLGRP_IPV4_ROUTE
: RTNLGRP_IPV6_ROUTE
;
296 route_table_change(const struct route_table_msg
*change OVS_UNUSED
,
297 void *aux OVS_UNUSED
)
299 route_table_valid
= false;
303 route_table_handle_msg(const struct route_table_msg
*change
)
305 if (change
->relevant
&& change
->nlmsg_type
== RTM_NEWROUTE
) {
306 const struct route_data
*rd
= &change
->rd
;
308 ovs_router_insert(rd
->mark
, &rd
->rta_dst
, rd
->rtm_dst_len
,
309 rd
->ifname
, &rd
->rta_gw
);
314 route_map_clear(void)
320 route_table_fallback_lookup(const struct in6_addr
*ip6_dst OVS_UNUSED
,
321 char name
[] OVS_UNUSED
,
322 struct in6_addr
*gw6
)
332 name_table_init(void)
334 name_notifier
= rtnetlink_notifier_create(name_table_change
, NULL
);
339 name_table_change(const struct rtnetlink_change
*change
,
340 void *aux OVS_UNUSED
)
342 /* Changes to interface status can cause routing table changes that some
343 * versions of the linux kernel do not advertise for some reason. */
344 route_table_valid
= false;
346 if (change
&& change
->nlmsg_type
== RTM_DELLINK
) {
347 if (change
->ifname
) {
348 tnl_port_map_delete_ipdev(change
->ifname
);