clang: Pass objects, not their addresses, to thread-safety macros.
[sliver-openvswitch.git] / ofproto / tunnel.c
1 /* Copyright (c) 2013 Nicira, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License. */
14
15 #include <config.h>
16 #include "tunnel.h"
17
18 #include <errno.h>
19
20 #include "byte-order.h"
21 #include "dynamic-string.h"
22 #include "hash.h"
23 #include "hmap.h"
24 #include "netdev.h"
25 #include "odp-util.h"
26 #include "packets.h"
27 #include "smap.h"
28 #include "socket-util.h"
29 #include "tunnel.h"
30 #include "vlog.h"
31
32 VLOG_DEFINE_THIS_MODULE(tunnel);
33
34 struct tnl_match {
35     ovs_be64 in_key;
36     ovs_be32 ip_src;
37     ovs_be32 ip_dst;
38     odp_port_t odp_port;
39     uint32_t skb_mark;
40     bool in_key_flow;
41     bool ip_src_flow;
42     bool ip_dst_flow;
43 };
44
45 struct tnl_port {
46     struct hmap_node ofport_node;
47     struct hmap_node match_node;
48
49     const struct ofport_dpif *ofport;
50     unsigned int netdev_seq;
51     struct netdev *netdev;
52
53     struct tnl_match match;
54 };
55
56 static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER;
57
58 static struct hmap tnl_match_map__ = HMAP_INITIALIZER(&tnl_match_map__);
59 static struct hmap *tnl_match_map OVS_GUARDED_BY(rwlock) = &tnl_match_map__;
60
61 static struct hmap ofport_map__ = HMAP_INITIALIZER(&ofport_map__);
62 static struct hmap *ofport_map OVS_GUARDED_BY(rwlock) = &ofport_map__;
63
64 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
65 static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60);
66
67 static struct tnl_port *tnl_find(struct tnl_match *) OVS_REQ_RDLOCK(rwlock);
68 static struct tnl_port *tnl_find_exact(struct tnl_match *)
69     OVS_REQ_RDLOCK(rwlock);
70 static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *)
71     OVS_REQ_RDLOCK(rwlock);
72
73 static uint32_t tnl_hash(struct tnl_match *);
74 static void tnl_match_fmt(const struct tnl_match *, struct ds *);
75 static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock);
76 static void tnl_port_mod_log(const struct tnl_port *, const char *action)
77     OVS_REQ_RDLOCK(rwlock);
78 static const char *tnl_port_get_name(const struct tnl_port *)
79     OVS_REQ_RDLOCK(rwlock);
80 static void tnl_port_del__(const struct ofport_dpif *) OVS_REQ_WRLOCK(rwlock);
81
82 static bool
83 tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
84                odp_port_t odp_port, bool warn)
85     OVS_REQ_WRLOCK(rwlock)
86 {
87     const struct netdev_tunnel_config *cfg;
88     struct tnl_port *existing_port;
89     struct tnl_port *tnl_port;
90
91     cfg = netdev_get_tunnel_config(netdev);
92     ovs_assert(cfg);
93
94     tnl_port = xzalloc(sizeof *tnl_port);
95     tnl_port->ofport = ofport;
96     tnl_port->netdev = netdev_ref(netdev);
97     tnl_port->netdev_seq = netdev_change_seq(tnl_port->netdev);
98
99     tnl_port->match.in_key = cfg->in_key;
100     tnl_port->match.ip_src = cfg->ip_src;
101     tnl_port->match.ip_dst = cfg->ip_dst;
102     tnl_port->match.ip_src_flow = cfg->ip_src_flow;
103     tnl_port->match.ip_dst_flow = cfg->ip_dst_flow;
104     tnl_port->match.skb_mark = cfg->ipsec ? IPSEC_MARK : 0;
105     tnl_port->match.in_key_flow = cfg->in_key_flow;
106     tnl_port->match.odp_port = odp_port;
107
108     existing_port = tnl_find_exact(&tnl_port->match);
109     if (existing_port) {
110         if (warn) {
111             struct ds ds = DS_EMPTY_INITIALIZER;
112             tnl_match_fmt(&tnl_port->match, &ds);
113             VLOG_WARN("%s: attempting to add tunnel port with same config as "
114                       "port '%s' (%s)", tnl_port_get_name(tnl_port),
115                       tnl_port_get_name(existing_port), ds_cstr(&ds));
116             ds_destroy(&ds);
117             free(tnl_port);
118         }
119         return false;
120     }
121
122     hmap_insert(ofport_map, &tnl_port->ofport_node, hash_pointer(ofport, 0));
123     hmap_insert(tnl_match_map, &tnl_port->match_node,
124                 tnl_hash(&tnl_port->match));
125     tnl_port_mod_log(tnl_port, "adding");
126     return true;
127 }
128
129 /* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's
130  * must be added before they can be used by the module. 'ofport' must be a
131  * tunnel. */
132 void
133 tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev,
134              odp_port_t odp_port) OVS_EXCLUDED(rwlock)
135 {
136     ovs_rwlock_wrlock(&rwlock);
137     tnl_port_add__(ofport, netdev, odp_port, true);
138     ovs_rwlock_unlock(&rwlock);
139 }
140
141 /* Checks if the tunnel represented by 'ofport' reconfiguration due to changes
142  * in its netdev_tunnel_config.  If it does, returns true. Otherwise, returns
143  * false.  'ofport' and 'odp_port' should be the same as would be passed to
144  * tnl_port_add(). */
145 bool
146 tnl_port_reconfigure(const struct ofport_dpif *ofport,
147                      const struct netdev *netdev, odp_port_t odp_port)
148     OVS_EXCLUDED(rwlock)
149 {
150     struct tnl_port *tnl_port;
151     bool changed = false;
152
153     ovs_rwlock_wrlock(&rwlock);
154     tnl_port = tnl_find_ofport(ofport);
155     if (!tnl_port) {
156         changed = tnl_port_add__(ofport, netdev, odp_port, false);
157     } else if (tnl_port->netdev != netdev
158                || tnl_port->match.odp_port != odp_port
159                || tnl_port->netdev_seq != netdev_change_seq(netdev)) {
160         VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port));
161         tnl_port_del__(ofport);
162         tnl_port_add__(ofport, netdev, odp_port, true);
163         changed = true;
164     }
165     ovs_rwlock_unlock(&rwlock);
166     return changed;
167 }
168
169 static void
170 tnl_port_del__(const struct ofport_dpif *ofport) OVS_REQ_WRLOCK(rwlock)
171 {
172     struct tnl_port *tnl_port;
173
174     if (!ofport) {
175         return;
176     }
177
178     tnl_port = tnl_find_ofport(ofport);
179     if (tnl_port) {
180         tnl_port_mod_log(tnl_port, "removing");
181         hmap_remove(tnl_match_map, &tnl_port->match_node);
182         hmap_remove(ofport_map, &tnl_port->ofport_node);
183         netdev_close(tnl_port->netdev);
184         free(tnl_port);
185     }
186 }
187
188 /* Removes 'ofport' from the module. */
189 void
190 tnl_port_del(const struct ofport_dpif *ofport) OVS_EXCLUDED(rwlock)
191 {
192     ovs_rwlock_wrlock(&rwlock);
193     tnl_port_del__(ofport);
194     ovs_rwlock_unlock(&rwlock);
195 }
196
197 /* Looks in the table of tunnels for a tunnel matching the metadata in 'flow'.
198  * Returns the 'ofport' corresponding to the new in_port, or a null pointer if
199  * none is found.
200  *
201  * Callers should verify that 'flow' needs to be received by calling
202  * tnl_port_should_receive() before this function. */
203 const struct ofport_dpif *
204 tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock)
205 {
206     char *pre_flow_str = NULL;
207     const struct ofport_dpif *ofport;
208     struct tnl_port *tnl_port;
209     struct tnl_match match;
210
211     memset(&match, 0, sizeof match);
212     match.odp_port = flow->in_port.odp_port;
213     match.ip_src = flow->tunnel.ip_dst;
214     match.ip_dst = flow->tunnel.ip_src;
215     match.in_key = flow->tunnel.tun_id;
216     match.skb_mark = flow->skb_mark;
217
218     ovs_rwlock_rdlock(&rwlock);
219     tnl_port = tnl_find(&match);
220     ofport = tnl_port ? tnl_port->ofport : NULL;
221     if (!tnl_port) {
222         struct ds ds = DS_EMPTY_INITIALIZER;
223
224         tnl_match_fmt(&match, &ds);
225         VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", ds_cstr(&ds));
226         ds_destroy(&ds);
227         goto out;
228     }
229
230     if (!VLOG_DROP_DBG(&dbg_rl)) {
231         pre_flow_str = flow_to_string(flow);
232     }
233
234     if (pre_flow_str) {
235         char *post_flow_str = flow_to_string(flow);
236         char *tnl_str = tnl_port_fmt(tnl_port);
237         VLOG_DBG("flow received\n"
238                  "%s"
239                  " pre: %s\n"
240                  "post: %s",
241                  tnl_str, pre_flow_str, post_flow_str);
242         free(tnl_str);
243         free(pre_flow_str);
244         free(post_flow_str);
245     }
246
247 out:
248     ovs_rwlock_unlock(&rwlock);
249     return ofport;
250 }
251
252 /* Given that 'flow' should be output to the ofport corresponding to
253  * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath
254  * port that the output should happen on.  May return ODPP_NONE if the output
255  * shouldn't occur. */
256 odp_port_t
257 tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
258               struct flow_wildcards *wc) OVS_EXCLUDED(rwlock)
259 {
260     const struct netdev_tunnel_config *cfg;
261     struct tnl_port *tnl_port;
262     char *pre_flow_str = NULL;
263     odp_port_t out_port;
264
265     ovs_rwlock_rdlock(&rwlock);
266     tnl_port = tnl_find_ofport(ofport);
267     out_port = tnl_port ? tnl_port->match.odp_port : ODPP_NONE;
268     if (!tnl_port) {
269         goto out;
270     }
271
272     cfg = netdev_get_tunnel_config(tnl_port->netdev);
273     ovs_assert(cfg);
274
275     if (!VLOG_DROP_DBG(&dbg_rl)) {
276         pre_flow_str = flow_to_string(flow);
277     }
278
279     if (!cfg->ip_src_flow) {
280         flow->tunnel.ip_src = tnl_port->match.ip_src;
281     }
282     if (!cfg->ip_dst_flow) {
283         flow->tunnel.ip_dst = tnl_port->match.ip_dst;
284     }
285     flow->skb_mark = tnl_port->match.skb_mark;
286
287     if (!cfg->out_key_flow) {
288         flow->tunnel.tun_id = cfg->out_key;
289     }
290
291     if (cfg->ttl_inherit && is_ip_any(flow)) {
292         wc->masks.nw_ttl = 0xff;
293         flow->tunnel.ip_ttl = flow->nw_ttl;
294     } else {
295         flow->tunnel.ip_ttl = cfg->ttl;
296     }
297
298     if (cfg->tos_inherit && is_ip_any(flow)) {
299         wc->masks.nw_tos = 0xff;
300         flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK;
301     } else {
302         flow->tunnel.ip_tos = cfg->tos;
303     }
304
305     /* ECN fields are always inherited. */
306     if (is_ip_any(flow)) {
307         wc->masks.nw_tos |= IP_ECN_MASK;
308     }
309
310     if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) {
311         flow->tunnel.ip_tos |= IP_ECN_ECT_0;
312     } else {
313         flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK;
314     }
315
316     flow->tunnel.flags = (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0)
317         | (cfg->csum ? FLOW_TNL_F_CSUM : 0)
318         | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0);
319
320     if (pre_flow_str) {
321         char *post_flow_str = flow_to_string(flow);
322         char *tnl_str = tnl_port_fmt(tnl_port);
323         VLOG_DBG("flow sent\n"
324                  "%s"
325                  " pre: %s\n"
326                  "post: %s",
327                  tnl_str, pre_flow_str, post_flow_str);
328         free(tnl_str);
329         free(pre_flow_str);
330         free(post_flow_str);
331     }
332
333 out:
334     ovs_rwlock_unlock(&rwlock);
335     return out_port;
336 }
337
338 static uint32_t
339 tnl_hash(struct tnl_match *match)
340 {
341     BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0);
342     return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0);
343 }
344
345 static struct tnl_port *
346 tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock)
347 {
348     struct tnl_port *tnl_port;
349
350     HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0),
351                              ofport_map) {
352         if (tnl_port->ofport == ofport) {
353             return tnl_port;
354         }
355     }
356     return NULL;
357 }
358
359 static struct tnl_port *
360 tnl_find_exact(struct tnl_match *match) OVS_REQ_RDLOCK(rwlock)
361 {
362     struct tnl_port *tnl_port;
363
364     HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match),
365                              tnl_match_map) {
366         if (!memcmp(match, &tnl_port->match, sizeof *match)) {
367             return tnl_port;
368         }
369     }
370     return NULL;
371 }
372
373 static struct tnl_port *
374 tnl_find(struct tnl_match *match_) OVS_REQ_RDLOCK(rwlock)
375 {
376     struct tnl_match match = *match_;
377     struct tnl_port *tnl_port;
378
379     /* remote_ip, local_ip, in_key */
380     tnl_port = tnl_find_exact(&match);
381     if (tnl_port) {
382         return tnl_port;
383     }
384
385     /* remote_ip, in_key */
386     match.ip_src = 0;
387     tnl_port = tnl_find_exact(&match);
388     if (tnl_port) {
389         return tnl_port;
390     }
391     match.ip_src = match_->ip_src;
392
393     /* remote_ip, local_ip */
394     match.in_key = 0;
395     match.in_key_flow = true;
396     tnl_port = tnl_find_exact(&match);
397     if (tnl_port) {
398         return tnl_port;
399     }
400
401     /* remote_ip */
402     match.ip_src = 0;
403     tnl_port = tnl_find_exact(&match);
404     if (tnl_port) {
405         return tnl_port;
406     }
407
408     /* Flow-based remote */
409     match.ip_dst = 0;
410     match.ip_dst_flow = true;
411     tnl_port = tnl_find_exact(&match);
412     if (tnl_port) {
413         return tnl_port;
414     }
415
416     /* Flow-based everything */
417     match.ip_src = 0;
418     match.ip_src_flow = true;
419     tnl_port = tnl_find_exact(&match);
420     if (tnl_port) {
421         return tnl_port;
422     }
423
424     return NULL;
425 }
426
427 static void
428 tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
429     OVS_REQ_RDLOCK(rwlock)
430 {
431     if (!match->ip_dst_flow) {
432         ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
433                       IP_ARGS(match->ip_dst));
434     } else if (!match->ip_src_flow) {
435         ds_put_format(ds, IP_FMT"->flow", IP_ARGS(match->ip_src));
436     } else {
437         ds_put_cstr(ds, "flow->flow");
438     }
439
440     if (match->in_key_flow) {
441         ds_put_cstr(ds, ", key=flow");
442     } else {
443         ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
444     }
445
446     ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
447     ds_put_format(ds, ", skb mark=%"PRIu32, match->skb_mark);
448 }
449
450 static void
451 tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action)
452     OVS_REQ_RDLOCK(rwlock)
453 {
454     if (VLOG_IS_DBG_ENABLED()) {
455         struct ds ds = DS_EMPTY_INITIALIZER;
456
457         tnl_match_fmt(&tnl_port->match, &ds);
458         VLOG_INFO("%s tunnel port %s (%s)", action,
459                   tnl_port_get_name(tnl_port), ds_cstr(&ds));
460         ds_destroy(&ds);
461     }
462 }
463
464 static char *
465 tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
466 {
467     const struct netdev_tunnel_config *cfg =
468         netdev_get_tunnel_config(tnl_port->netdev);
469     struct ds ds = DS_EMPTY_INITIALIZER;
470
471     ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port,
472                   tnl_port_get_name(tnl_port),
473                   netdev_get_type(tnl_port->netdev));
474     tnl_match_fmt(&tnl_port->match, &ds);
475
476     if (cfg->out_key != cfg->in_key ||
477         cfg->out_key_present != cfg->in_key_present ||
478         cfg->out_key_flow != cfg->in_key_flow) {
479         ds_put_cstr(&ds, ", out_key=");
480         if (!cfg->out_key_present) {
481             ds_put_cstr(&ds, "none");
482         } else if (cfg->out_key_flow) {
483             ds_put_cstr(&ds, "flow");
484         } else {
485             ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key));
486         }
487     }
488
489     if (cfg->ttl_inherit) {
490         ds_put_cstr(&ds, ", ttl=inherit");
491     } else {
492         ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl);
493     }
494
495     if (cfg->tos_inherit) {
496         ds_put_cstr(&ds, ", tos=inherit");
497     } else if (cfg->tos) {
498         ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos);
499     }
500
501     if (!cfg->dont_fragment) {
502         ds_put_cstr(&ds, ", df=false");
503     }
504
505     if (cfg->csum) {
506         ds_put_cstr(&ds, ", csum=true");
507     }
508
509     ds_put_cstr(&ds, ")\n");
510
511     return ds_steal_cstr(&ds);
512 }
513
514 static const char *
515 tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
516 {
517     return netdev_get_name(tnl_port->netdev);
518 }