dpctl: Fix crash.
[cascardo/ovs.git] / lib / route-table.c
1 /*
2  * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
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
21 #include <errno.h>
22 #include <arpa/inet.h>
23 #include <sys/socket.h>
24 #include <linux/rtnetlink.h>
25 #include <net/if.h>
26
27 #include "hash.h"
28 #include "netlink.h"
29 #include "netlink-notifier.h"
30 #include "netlink-socket.h"
31 #include "ofpbuf.h"
32 #include "ovs-router.h"
33 #include "ovs-router-linux.h"
34 #include "rtnetlink-link.h"
35 #include "vlog.h"
36
37 VLOG_DEFINE_THIS_MODULE(route_table);
38
39 struct route_data {
40     /* Copied from struct rtmsg. */
41     unsigned char rtm_dst_len;
42
43     /* Extracted from Netlink attributes. */
44     ovs_be32 rta_dst; /* 0 if missing. */
45     ovs_be32 rta_gw;
46     char ifname[IFNAMSIZ]; /* Interface name. */
47 };
48
49 /* A digested version of a route message sent down by the kernel to indicate
50  * that a route has changed. */
51 struct route_table_msg {
52     bool relevant;        /* Should this message be processed? */
53     int nlmsg_type;       /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */
54     struct route_data rd; /* Data parsed from this message. */
55 };
56
57 static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER;
58 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
59
60 /* Global change number for route-table, which should be incremented
61  * every time route_table_reset() is called.  */
62 static uint64_t rt_change_seq;
63
64 static unsigned int register_count = 0;
65 static struct nln *nln = NULL;
66 static struct route_table_msg rtmsg;
67 static struct nln_notifier *route_notifier = NULL;
68 static struct nln_notifier *name_notifier = NULL;
69
70 static bool route_table_valid = false;
71
72 static int route_table_reset(void);
73 static void route_table_handle_msg(const struct route_table_msg *);
74 static bool route_table_parse(struct ofpbuf *, struct route_table_msg *);
75 static void route_table_change(const struct route_table_msg *, void *);
76 static void route_map_clear(void);
77
78 static void name_table_init(void);
79 static void name_table_uninit(void);
80 static void name_table_change(const struct rtnetlink_link_change *, void *);
81
82 uint64_t
83 route_table_get_change_seq(void)
84 {
85     return rt_change_seq;
86 }
87
88 /* Users of the route_table module should register themselves with this
89  * function before making any other route_table function calls. */
90 void
91 route_table_register(void)
92     OVS_EXCLUDED(route_table_mutex)
93 {
94     ovs_mutex_lock(&route_table_mutex);
95     if (!register_count) {
96         ovs_assert(!nln);
97         ovs_assert(!route_notifier);
98
99         ovs_router_init();
100         nln = nln_create(NETLINK_ROUTE, RTNLGRP_IPV4_ROUTE,
101                          (nln_parse_func *) route_table_parse, &rtmsg);
102
103         route_notifier =
104             nln_notifier_create(nln, (nln_notify_func *) route_table_change,
105                                 NULL);
106
107         route_table_reset();
108         name_table_init();
109     }
110
111     register_count++;
112     ovs_mutex_unlock(&route_table_mutex);
113 }
114
115 /* Users of the route_table module should unregister themselves with this
116  * function when they will no longer be making any more route_table fuction
117  * calls. */
118 void
119 route_table_unregister(void)
120     OVS_EXCLUDED(route_table_mutex)
121 {
122     ovs_mutex_lock(&route_table_mutex);
123     register_count--;
124
125     if (!register_count) {
126         nln_notifier_destroy(route_notifier);
127         route_notifier = NULL;
128         nln_destroy(nln);
129         nln = NULL;
130
131         route_map_clear();
132         name_table_uninit();
133     }
134     ovs_mutex_unlock(&route_table_mutex);
135 }
136
137 /* Run periodically to update the locally maintained routing table. */
138 void
139 route_table_run(void)
140     OVS_EXCLUDED(route_table_mutex)
141 {
142     ovs_mutex_lock(&route_table_mutex);
143     if (nln) {
144         rtnetlink_link_run();
145         nln_run(nln);
146
147         if (!route_table_valid) {
148             route_table_reset();
149         }
150     }
151     ovs_mutex_unlock(&route_table_mutex);
152 }
153
154 /* Causes poll_block() to wake up when route_table updates are required. */
155 void
156 route_table_wait(void)
157     OVS_EXCLUDED(route_table_mutex)
158 {
159     ovs_mutex_lock(&route_table_mutex);
160     if (nln) {
161         rtnetlink_link_wait();
162         nln_wait(nln);
163     }
164     ovs_mutex_unlock(&route_table_mutex);
165 }
166
167 static int
168 route_table_reset(void)
169 {
170     struct nl_dump dump;
171     struct rtgenmsg *rtmsg;
172     uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
173     struct ofpbuf request, reply, buf;
174
175     route_map_clear();
176     route_table_valid = true;
177     rt_change_seq++;
178
179     ofpbuf_init(&request, 0);
180
181     nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETROUTE, NLM_F_REQUEST);
182
183     rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
184     rtmsg->rtgen_family = AF_INET;
185
186     nl_dump_start(&dump, NETLINK_ROUTE, &request);
187     ofpbuf_uninit(&request);
188
189     ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
190     while (nl_dump_next(&dump, &reply, &buf)) {
191         struct route_table_msg msg;
192
193         if (route_table_parse(&reply, &msg)) {
194             route_table_handle_msg(&msg);
195         }
196     }
197     ofpbuf_uninit(&buf);
198
199     return nl_dump_done(&dump);
200 }
201
202
203 static bool
204 route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
205 {
206     bool parsed;
207
208     static const struct nl_policy policy[] = {
209         [RTA_DST] = { .type = NL_A_U32, .optional = true  },
210         [RTA_OIF] = { .type = NL_A_U32, .optional = false },
211         [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true },
212     };
213
214     struct nlattr *attrs[ARRAY_SIZE(policy)];
215
216     parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg),
217                              policy, attrs, ARRAY_SIZE(policy));
218
219     if (parsed) {
220         const struct rtmsg *rtm;
221         const struct nlmsghdr *nlmsg;
222         int rta_oif;      /* Output interface index. */
223
224         nlmsg = ofpbuf_data(buf);
225         rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm);
226
227         if (rtm->rtm_family != AF_INET) {
228             VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
229             return false;
230         }
231
232         memset(change, 0, sizeof *change);
233         change->relevant = true;
234
235         if (rtm->rtm_scope == RT_SCOPE_NOWHERE) {
236             change->relevant = false;
237         }
238
239         if (rtm->rtm_type != RTN_UNICAST &&
240             rtm->rtm_type != RTN_LOCAL) {
241             change->relevant = false;
242         }
243         change->nlmsg_type     = nlmsg->nlmsg_type;
244         change->rd.rtm_dst_len = rtm->rtm_dst_len;
245         rta_oif = nl_attr_get_u32(attrs[RTA_OIF]);
246
247         if (!if_indextoname(rta_oif, change->rd.ifname)) {
248             int error = errno;
249
250             VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s",
251                         rta_oif, ovs_strerror(error));
252             return false;
253         }
254
255         if (attrs[RTA_DST]) {
256             change->rd.rta_dst = nl_attr_get_be32(attrs[RTA_DST]);
257         }
258         if (attrs[RTA_GATEWAY]) {
259             change->rd.rta_gw = nl_attr_get_be32(attrs[RTA_GATEWAY]);
260         }
261
262
263     } else {
264         VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
265     }
266
267     return parsed;
268 }
269
270 static void
271 route_table_change(const struct route_table_msg *change OVS_UNUSED,
272                    void *aux OVS_UNUSED)
273 {
274     route_table_valid = false;
275 }
276
277 static void
278 route_table_handle_msg(const struct route_table_msg *change)
279 {
280     if (change->relevant && change->nlmsg_type == RTM_NEWROUTE) {
281         const struct route_data *rd = &change->rd;
282
283         ovs_router_insert(rd->rta_dst, rd->rtm_dst_len,
284                           rd->ifname, rd->rta_gw);
285     }
286 }
287
288 static void
289 route_map_clear(void)
290 {
291     ovs_router_flush();
292 }
293
294 \f
295 /* name_table . */
296
297 static void
298 name_table_init(void)
299 {
300     name_notifier = rtnetlink_link_notifier_create(name_table_change, NULL);
301 }
302
303 static void
304 name_table_uninit(void)
305 {
306     rtnetlink_link_notifier_destroy(name_notifier);
307     name_notifier = NULL;
308 }
309
310 static void
311 name_table_change(const struct rtnetlink_link_change *change OVS_UNUSED,
312                   void *aux OVS_UNUSED)
313 {
314     /* Changes to interface status can cause routing table changes that some
315      * versions of the linux kernel do not advertise for some reason. */
316     route_table_valid = false;
317 }