route-table: get rid of name-table
[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 "hmap.h"
29 #include "netlink.h"
30 #include "netlink-notifier.h"
31 #include "netlink-socket.h"
32 #include "ofpbuf.h"
33 #include "rtnetlink-link.h"
34 #include "vlog.h"
35
36 VLOG_DEFINE_THIS_MODULE(route_table);
37
38 struct route_data {
39     /* Copied from struct rtmsg. */
40     unsigned char rtm_dst_len;
41
42     /* Extracted from Netlink attributes. */
43     uint32_t rta_dst; /* Destination in host byte order. 0 if missing. */
44     char ifname[IFNAMSIZ]; /* Interface name. */
45 };
46
47 /* A digested version of a route message sent down by the kernel to indicate
48  * that a route has changed. */
49 struct route_table_msg {
50     bool relevant;        /* Should this message be processed? */
51     int nlmsg_type;       /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */
52     struct route_data rd; /* Data parsed from this message. */
53 };
54
55 struct route_node {
56     struct hmap_node node; /* Node in route_map. */
57     struct route_data rd;  /* Data associated with this node. */
58 };
59
60 static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER;
61 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
62
63 /* Global change number for route-table, which should be incremented
64  * every time route_table_reset() is called.  */
65 static uint64_t rt_change_seq;
66
67 static unsigned int register_count = 0;
68 static struct nln *nln = NULL;
69 static struct route_table_msg rtmsg;
70 static struct nln_notifier *route_notifier = NULL;
71 static struct nln_notifier *name_notifier = NULL;
72
73 static bool route_table_valid = false;
74 static struct hmap route_map;
75
76 static int route_table_reset(void);
77 static void route_table_handle_msg(const struct route_table_msg *);
78 static bool route_table_parse(struct ofpbuf *, struct route_table_msg *);
79 static void route_table_change(const struct route_table_msg *, void *);
80 static struct route_node *route_node_lookup(const struct route_data *);
81 static struct route_node *route_node_lookup_by_ip(uint32_t ip);
82 static void route_map_clear(void);
83 static uint32_t hash_route_data(const struct route_data *);
84
85 static void name_table_init(void);
86 static void name_table_uninit(void);
87 static void name_table_change(const struct rtnetlink_link_change *, void *);
88
89 /* Populates 'name' with the name of the interface traffic destined for 'ip'
90  * is likely to egress out of.
91  *
92  * Returns true if successful, otherwise false. */
93 bool
94 route_table_get_name(ovs_be32 ip_, char name[IFNAMSIZ])
95     OVS_REQUIRES(route_table_mutex)
96 {
97     struct route_node *rn;
98     uint32_t ip = ntohl(ip_);
99     bool res = false;
100
101     ovs_mutex_lock(&route_table_mutex);
102     if (!route_table_valid) {
103         route_table_reset();
104     }
105
106     rn = route_node_lookup_by_ip(ip);
107
108     if (rn) {
109         ovs_strlcpy(name, rn->rd.ifname, IFNAMSIZ);
110         res = true;
111         goto out;
112     }
113
114     /* Choose a default route. */
115     HMAP_FOR_EACH(rn, node, &route_map) {
116         if (rn->rd.rta_dst == 0 && rn->rd.rtm_dst_len == 0) {
117             ovs_strlcpy(name, rn->rd.ifname, IFNAMSIZ);
118             res = true;
119             break;
120         }
121     }
122
123 out:
124     ovs_mutex_unlock(&route_table_mutex);
125     return res;
126 }
127
128 uint64_t
129 route_table_get_change_seq(void)
130 {
131     return rt_change_seq;
132 }
133
134 /* Users of the route_table module should register themselves with this
135  * function before making any other route_table function calls. */
136 void
137 route_table_register(void)
138     OVS_EXCLUDED(route_table_mutex)
139 {
140     ovs_mutex_lock(&route_table_mutex);
141     if (!register_count) {
142         ovs_assert(!nln);
143         ovs_assert(!route_notifier);
144
145         nln = nln_create(NETLINK_ROUTE, RTNLGRP_IPV4_ROUTE,
146                          (nln_parse_func *) route_table_parse, &rtmsg);
147
148         route_notifier =
149             nln_notifier_create(nln, (nln_notify_func *) route_table_change,
150                                 NULL);
151
152         hmap_init(&route_map);
153         route_table_reset();
154         name_table_init();
155     }
156
157     register_count++;
158     ovs_mutex_unlock(&route_table_mutex);
159 }
160
161 /* Users of the route_table module should unregister themselves with this
162  * function when they will no longer be making any more route_table fuction
163  * calls. */
164 void
165 route_table_unregister(void)
166     OVS_EXCLUDED(route_table_mutex)
167 {
168     ovs_mutex_lock(&route_table_mutex);
169     register_count--;
170
171     if (!register_count) {
172         nln_notifier_destroy(route_notifier);
173         route_notifier = NULL;
174         nln_destroy(nln);
175         nln = NULL;
176
177         route_map_clear();
178         hmap_destroy(&route_map);
179         name_table_uninit();
180     }
181     ovs_mutex_unlock(&route_table_mutex);
182 }
183
184 /* Run periodically to update the locally maintained routing table. */
185 void
186 route_table_run(void)
187     OVS_EXCLUDED(route_table_mutex)
188 {
189     ovs_mutex_lock(&route_table_mutex);
190     if (nln) {
191         rtnetlink_link_run();
192         nln_run(nln);
193
194         if (!route_table_valid) {
195             route_table_reset();
196         }
197     }
198     ovs_mutex_unlock(&route_table_mutex);
199 }
200
201 /* Causes poll_block() to wake up when route_table updates are required. */
202 void
203 route_table_wait(void)
204     OVS_EXCLUDED(route_table_mutex)
205 {
206     ovs_mutex_lock(&route_table_mutex);
207     if (nln) {
208         rtnetlink_link_wait();
209         nln_wait(nln);
210     }
211     ovs_mutex_unlock(&route_table_mutex);
212 }
213
214 static int
215 route_table_reset(void)
216 {
217     struct nl_dump dump;
218     struct rtgenmsg *rtmsg;
219     uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
220     struct ofpbuf request, reply, buf;
221
222     route_map_clear();
223     route_table_valid = true;
224     rt_change_seq++;
225
226     ofpbuf_init(&request, 0);
227
228     nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETROUTE, NLM_F_REQUEST);
229
230     rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
231     rtmsg->rtgen_family = AF_INET;
232
233     nl_dump_start(&dump, NETLINK_ROUTE, &request);
234     ofpbuf_uninit(&request);
235
236     ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
237     while (nl_dump_next(&dump, &reply, &buf)) {
238         struct route_table_msg msg;
239
240         if (route_table_parse(&reply, &msg)) {
241             route_table_handle_msg(&msg);
242         }
243     }
244     ofpbuf_uninit(&buf);
245
246     return nl_dump_done(&dump);
247 }
248
249
250 static bool
251 route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
252 {
253     bool parsed;
254
255     static const struct nl_policy policy[] = {
256         [RTA_DST] = { .type = NL_A_U32, .optional = true  },
257         [RTA_OIF] = { .type = NL_A_U32, .optional = false },
258     };
259
260     struct nlattr *attrs[ARRAY_SIZE(policy)];
261
262     parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg),
263                              policy, attrs, ARRAY_SIZE(policy));
264
265     if (parsed) {
266         const struct rtmsg *rtm;
267         const struct nlmsghdr *nlmsg;
268         int rta_oif;      /* Output interface index. */
269
270         nlmsg = ofpbuf_data(buf);
271         rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm);
272
273         if (rtm->rtm_family != AF_INET) {
274             VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message");
275             return false;
276         }
277
278         memset(change, 0, sizeof *change);
279         change->relevant = true;
280
281         if (rtm->rtm_scope == RT_SCOPE_NOWHERE) {
282             change->relevant = false;
283         }
284
285         if (rtm->rtm_type != RTN_UNICAST &&
286             rtm->rtm_type != RTN_LOCAL) {
287             change->relevant = false;
288         }
289         change->nlmsg_type     = nlmsg->nlmsg_type;
290         change->rd.rtm_dst_len = rtm->rtm_dst_len;
291         rta_oif = nl_attr_get_u32(attrs[RTA_OIF]);
292
293         if (!if_indextoname(rta_oif, change->rd.ifname)) {
294             int error = errno;
295
296             VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s",
297                         rta_oif, ovs_strerror(error));
298             return false;
299         }
300
301         if (attrs[RTA_DST]) {
302             change->rd.rta_dst = ntohl(nl_attr_get_be32(attrs[RTA_DST]));
303         }
304
305     } else {
306         VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
307     }
308
309     return parsed;
310 }
311
312 static void
313 route_table_change(const struct route_table_msg *change OVS_UNUSED,
314                    void *aux OVS_UNUSED)
315 {
316     route_table_valid = false;
317 }
318
319 static void
320 route_table_handle_msg(const struct route_table_msg *change)
321 {
322     if (change->relevant && change->nlmsg_type == RTM_NEWROUTE &&
323         !route_node_lookup(&change->rd)) {
324         struct route_node *rn;
325
326         rn = xzalloc(sizeof *rn);
327         memcpy(&rn->rd, &change->rd, sizeof change->rd);
328
329         hmap_insert(&route_map, &rn->node, hash_route_data(&rn->rd));
330     }
331 }
332
333 static struct route_node *
334 route_node_lookup(const struct route_data *rd)
335 {
336     struct route_node *rn;
337
338     HMAP_FOR_EACH_WITH_HASH(rn, node, hash_route_data(rd), &route_map) {
339         if (!memcmp(&rn->rd, rd, sizeof *rd)) {
340             return rn;
341         }
342     }
343
344     return NULL;
345 }
346
347 static struct route_node *
348 route_node_lookup_by_ip(uint32_t ip)
349 {
350     int dst_len;
351     struct route_node *rn, *rn_ret;
352
353     dst_len = -1;
354     rn_ret  = NULL;
355
356     HMAP_FOR_EACH(rn, node, &route_map) {
357         uint32_t mask = 0xffffffff << (32 - rn->rd.rtm_dst_len);
358
359         if (rn->rd.rta_dst == 0 && rn->rd.rtm_dst_len == 0) {
360             /* Default route. */
361             continue;
362         }
363
364         if (rn->rd.rtm_dst_len > dst_len &&
365             (ip & mask) == (rn->rd.rta_dst & mask)) {
366             rn_ret  = rn;
367             dst_len = rn->rd.rtm_dst_len;
368         }
369     }
370
371     return rn_ret;
372 }
373
374 static void
375 route_map_clear(void)
376 {
377     struct route_node *rn, *rn_next;
378
379     HMAP_FOR_EACH_SAFE(rn, rn_next, node, &route_map) {
380         hmap_remove(&route_map, &rn->node);
381         free(rn);
382     }
383 }
384
385 static uint32_t
386 hash_route_data(const struct route_data *rd)
387 {
388     return hash_bytes(rd, sizeof *rd, 0);
389 }
390 \f
391 /* name_table . */
392
393 static void
394 name_table_init(void)
395 {
396     name_notifier = rtnetlink_link_notifier_create(name_table_change, NULL);
397 }
398
399 static void
400 name_table_uninit(void)
401 {
402     rtnetlink_link_notifier_destroy(name_notifier);
403     name_notifier = NULL;
404 }
405
406 static void
407 name_table_change(const struct rtnetlink_link_change *change OVS_UNUSED,
408                   void *aux OVS_UNUSED)
409 {
410     /* Changes to interface status can cause routing table changes that some
411      * versions of the linux kernel do not advertise for some reason. */
412     route_table_valid = false;
413 }