tunnel: Consolidate action code for tunnel port receive.
[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 pkt_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.pkt_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.pkt_mark = flow->pkt_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 static bool
253 tnl_ecn_ok(const struct flow *base_flow, struct flow *flow)
254 {
255     if (is_ip_any(base_flow)
256         && (flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
257         if ((base_flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
258             VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
259                          " but is not ECN capable");
260             return false;
261         } else {
262             /* Set the ECN CE value in the tunneled packet. */
263             flow->nw_tos |= IP_ECN_CE;
264         }
265     }
266
267     return true;
268 }
269
270 /* Should be called at the beginning of action translation to initialize
271  * wildcards and perform any actions based on receiving on tunnel port.
272  *
273  * Returns false if the packet must be dropped. */
274 bool
275 tnl_xlate_init(const struct flow *base_flow, struct flow *flow,
276                struct flow_wildcards *wc)
277 {
278     if (tnl_port_should_receive(flow)) {
279         memset(&wc->masks.tunnel, 0xff, sizeof wc->masks.tunnel);
280         memset(&wc->masks.pkt_mark, 0xff, sizeof wc->masks.pkt_mark);
281
282         if (!tnl_ecn_ok(base_flow, flow)) {
283             return false;
284         }
285     }
286
287     return true;
288 }
289
290 /* Given that 'flow' should be output to the ofport corresponding to
291  * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath
292  * port that the output should happen on.  May return ODPP_NONE if the output
293  * shouldn't occur. */
294 odp_port_t
295 tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
296               struct flow_wildcards *wc) OVS_EXCLUDED(rwlock)
297 {
298     const struct netdev_tunnel_config *cfg;
299     struct tnl_port *tnl_port;
300     char *pre_flow_str = NULL;
301     odp_port_t out_port;
302
303     ovs_rwlock_rdlock(&rwlock);
304     tnl_port = tnl_find_ofport(ofport);
305     out_port = tnl_port ? tnl_port->match.odp_port : ODPP_NONE;
306     if (!tnl_port) {
307         goto out;
308     }
309
310     cfg = netdev_get_tunnel_config(tnl_port->netdev);
311     ovs_assert(cfg);
312
313     if (!VLOG_DROP_DBG(&dbg_rl)) {
314         pre_flow_str = flow_to_string(flow);
315     }
316
317     if (!cfg->ip_src_flow) {
318         flow->tunnel.ip_src = tnl_port->match.ip_src;
319     }
320     if (!cfg->ip_dst_flow) {
321         flow->tunnel.ip_dst = tnl_port->match.ip_dst;
322     }
323     flow->pkt_mark = tnl_port->match.pkt_mark;
324
325     if (!cfg->out_key_flow) {
326         flow->tunnel.tun_id = cfg->out_key;
327     }
328
329     if (cfg->ttl_inherit && is_ip_any(flow)) {
330         wc->masks.nw_ttl = 0xff;
331         flow->tunnel.ip_ttl = flow->nw_ttl;
332     } else {
333         flow->tunnel.ip_ttl = cfg->ttl;
334     }
335
336     if (cfg->tos_inherit && is_ip_any(flow)) {
337         wc->masks.nw_tos = 0xff;
338         flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK;
339     } else {
340         flow->tunnel.ip_tos = cfg->tos;
341     }
342
343     /* ECN fields are always inherited. */
344     if (is_ip_any(flow)) {
345         wc->masks.nw_tos |= IP_ECN_MASK;
346     }
347
348     if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) {
349         flow->tunnel.ip_tos |= IP_ECN_ECT_0;
350     } else {
351         flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK;
352     }
353
354     flow->tunnel.flags = (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0)
355         | (cfg->csum ? FLOW_TNL_F_CSUM : 0)
356         | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0);
357
358     if (pre_flow_str) {
359         char *post_flow_str = flow_to_string(flow);
360         char *tnl_str = tnl_port_fmt(tnl_port);
361         VLOG_DBG("flow sent\n"
362                  "%s"
363                  " pre: %s\n"
364                  "post: %s",
365                  tnl_str, pre_flow_str, post_flow_str);
366         free(tnl_str);
367         free(pre_flow_str);
368         free(post_flow_str);
369     }
370
371 out:
372     ovs_rwlock_unlock(&rwlock);
373     return out_port;
374 }
375
376 static uint32_t
377 tnl_hash(struct tnl_match *match)
378 {
379     BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0);
380     return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0);
381 }
382
383 static struct tnl_port *
384 tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock)
385 {
386     struct tnl_port *tnl_port;
387
388     HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0),
389                              ofport_map) {
390         if (tnl_port->ofport == ofport) {
391             return tnl_port;
392         }
393     }
394     return NULL;
395 }
396
397 static struct tnl_port *
398 tnl_find_exact(struct tnl_match *match) OVS_REQ_RDLOCK(rwlock)
399 {
400     struct tnl_port *tnl_port;
401
402     HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match),
403                              tnl_match_map) {
404         if (!memcmp(match, &tnl_port->match, sizeof *match)) {
405             return tnl_port;
406         }
407     }
408     return NULL;
409 }
410
411 static struct tnl_port *
412 tnl_find(struct tnl_match *match_) OVS_REQ_RDLOCK(rwlock)
413 {
414     struct tnl_match match = *match_;
415     struct tnl_port *tnl_port;
416
417     /* remote_ip, local_ip, in_key */
418     tnl_port = tnl_find_exact(&match);
419     if (tnl_port) {
420         return tnl_port;
421     }
422
423     /* remote_ip, in_key */
424     match.ip_src = 0;
425     tnl_port = tnl_find_exact(&match);
426     if (tnl_port) {
427         return tnl_port;
428     }
429     match.ip_src = match_->ip_src;
430
431     /* remote_ip, local_ip */
432     match.in_key = 0;
433     match.in_key_flow = true;
434     tnl_port = tnl_find_exact(&match);
435     if (tnl_port) {
436         return tnl_port;
437     }
438
439     /* remote_ip */
440     match.ip_src = 0;
441     tnl_port = tnl_find_exact(&match);
442     if (tnl_port) {
443         return tnl_port;
444     }
445
446     /* Flow-based remote */
447     match.ip_dst = 0;
448     match.ip_dst_flow = true;
449     tnl_port = tnl_find_exact(&match);
450     if (tnl_port) {
451         return tnl_port;
452     }
453
454     /* Flow-based everything */
455     match.ip_src = 0;
456     match.ip_src_flow = true;
457     tnl_port = tnl_find_exact(&match);
458     if (tnl_port) {
459         return tnl_port;
460     }
461
462     return NULL;
463 }
464
465 static void
466 tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
467     OVS_REQ_RDLOCK(rwlock)
468 {
469     if (!match->ip_dst_flow) {
470         ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
471                       IP_ARGS(match->ip_dst));
472     } else if (!match->ip_src_flow) {
473         ds_put_format(ds, IP_FMT"->flow", IP_ARGS(match->ip_src));
474     } else {
475         ds_put_cstr(ds, "flow->flow");
476     }
477
478     if (match->in_key_flow) {
479         ds_put_cstr(ds, ", key=flow");
480     } else {
481         ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
482     }
483
484     ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
485     ds_put_format(ds, ", pkt mark=%"PRIu32, match->pkt_mark);
486 }
487
488 static void
489 tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action)
490     OVS_REQ_RDLOCK(rwlock)
491 {
492     if (VLOG_IS_DBG_ENABLED()) {
493         struct ds ds = DS_EMPTY_INITIALIZER;
494
495         tnl_match_fmt(&tnl_port->match, &ds);
496         VLOG_INFO("%s tunnel port %s (%s)", action,
497                   tnl_port_get_name(tnl_port), ds_cstr(&ds));
498         ds_destroy(&ds);
499     }
500 }
501
502 static char *
503 tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
504 {
505     const struct netdev_tunnel_config *cfg =
506         netdev_get_tunnel_config(tnl_port->netdev);
507     struct ds ds = DS_EMPTY_INITIALIZER;
508
509     ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port,
510                   tnl_port_get_name(tnl_port),
511                   netdev_get_type(tnl_port->netdev));
512     tnl_match_fmt(&tnl_port->match, &ds);
513
514     if (cfg->out_key != cfg->in_key ||
515         cfg->out_key_present != cfg->in_key_present ||
516         cfg->out_key_flow != cfg->in_key_flow) {
517         ds_put_cstr(&ds, ", out_key=");
518         if (!cfg->out_key_present) {
519             ds_put_cstr(&ds, "none");
520         } else if (cfg->out_key_flow) {
521             ds_put_cstr(&ds, "flow");
522         } else {
523             ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key));
524         }
525     }
526
527     if (cfg->ttl_inherit) {
528         ds_put_cstr(&ds, ", ttl=inherit");
529     } else {
530         ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl);
531     }
532
533     if (cfg->tos_inherit) {
534         ds_put_cstr(&ds, ", tos=inherit");
535     } else if (cfg->tos) {
536         ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos);
537     }
538
539     if (!cfg->dont_fragment) {
540         ds_put_cstr(&ds, ", df=false");
541     }
542
543     if (cfg->csum) {
544         ds_put_cstr(&ds, ", csum=true");
545     }
546
547     ds_put_cstr(&ds, ")\n");
548
549     return ds_steal_cstr(&ds);
550 }
551
552 static const char *
553 tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
554 {
555     return netdev_get_name(tnl_port->netdev);
556 }