~james-page/ubuntu/saucy/openvswitch/1.12-snapshot

« back to all changes in this revision

Viewing changes to ofproto/tunnel.c

  • Committer: James Page
  • Date: 2013-08-21 10:16:57 UTC
  • mfrom: (1.1.20)
  • Revision ID: james.page@canonical.com-20130821101657-3o0z0qeiv5zkwlzi
New upstream snapshot

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
#include <errno.h>
19
19
 
20
 
#include "ofproto/ofproto-provider.h"
21
20
#include "byte-order.h"
22
21
#include "dynamic-string.h"
23
22
#include "hash.h"
24
23
#include "hmap.h"
25
 
#include "netdev-vport.h"
 
24
#include "netdev.h"
26
25
#include "odp-util.h"
27
26
#include "packets.h"
28
27
#include "smap.h"
30
29
#include "tunnel.h"
31
30
#include "vlog.h"
32
31
 
33
 
/* XXX:
34
 
 *
35
 
 * Disallow netdevs with names like "gre64_system" to prevent collisions. */
36
 
 
37
32
VLOG_DEFINE_THIS_MODULE(tunnel);
38
33
 
39
34
struct tnl_match {
40
35
    ovs_be64 in_key;
41
36
    ovs_be32 ip_src;
42
37
    ovs_be32 ip_dst;
43
 
    uint32_t odp_port;
 
38
    odp_port_t odp_port;
44
39
    uint32_t skb_mark;
45
40
    bool in_key_flow;
 
41
    bool ip_src_flow;
 
42
    bool ip_dst_flow;
46
43
};
47
44
 
48
45
struct tnl_port {
 
46
    struct hmap_node ofport_node;
49
47
    struct hmap_node match_node;
50
48
 
51
 
    const struct ofport *ofport;
 
49
    const struct ofport_dpif *ofport;
52
50
    unsigned int netdev_seq;
 
51
    struct netdev *netdev;
 
52
 
53
53
    struct tnl_match match;
54
54
};
55
55
 
56
 
static struct hmap tnl_match_map = HMAP_INITIALIZER(&tnl_match_map);
57
 
 
58
 
/* Returned to callers when their ofport will never be used to receive or send
59
 
 * tunnel traffic. Alternatively, we could ask the caller to delete their
60
 
 * ofport, but this would be unclean in the reconfguration case.  For the first
61
 
 * time, an ofproto provider would have to call ofproto_port_del() on itself.*/
62
 
static struct tnl_port void_tnl_port;
 
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
63
 
64
64
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
65
65
static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60);
66
66
 
67
 
static struct tnl_port *tnl_find(struct tnl_match *);
68
 
static struct tnl_port *tnl_find_exact(struct tnl_match *);
 
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
 
69
73
static uint32_t tnl_hash(struct tnl_match *);
70
74
static void tnl_match_fmt(const struct tnl_match *, struct ds *);
71
 
static char *tnl_port_fmt(const struct tnl_port *);
72
 
static void tnl_port_mod_log(const struct tnl_port *, const char *action);
73
 
static const char *tnl_port_get_name(const struct tnl_port *);
 
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);
74
81
 
75
 
static struct tnl_port *
76
 
tnl_port_add__(const struct ofport *ofport, uint32_t odp_port,
77
 
               bool warn)
 
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)
78
86
{
79
87
    const struct netdev_tunnel_config *cfg;
80
88
    struct tnl_port *existing_port;
81
89
    struct tnl_port *tnl_port;
82
90
 
83
 
    cfg = netdev_get_tunnel_config(ofport->netdev);
 
91
    cfg = netdev_get_tunnel_config(netdev);
84
92
    ovs_assert(cfg);
85
93
 
86
94
    tnl_port = xzalloc(sizeof *tnl_port);
87
95
    tnl_port->ofport = ofport;
88
 
    tnl_port->netdev_seq = netdev_change_seq(tnl_port->ofport->netdev);
 
96
    tnl_port->netdev = netdev_ref(netdev);
 
97
    tnl_port->netdev_seq = netdev_change_seq(tnl_port->netdev);
89
98
 
90
99
    tnl_port->match.in_key = cfg->in_key;
91
100
    tnl_port->match.ip_src = cfg->ip_src;
92
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;
93
104
    tnl_port->match.skb_mark = cfg->ipsec ? IPSEC_MARK : 0;
94
105
    tnl_port->match.in_key_flow = cfg->in_key_flow;
95
106
    tnl_port->match.odp_port = odp_port;
105
116
            ds_destroy(&ds);
106
117
            free(tnl_port);
107
118
        }
108
 
        return &void_tnl_port;
 
119
        return false;
109
120
    }
110
121
 
111
 
    hmap_insert(&tnl_match_map, &tnl_port->match_node,
 
122
    hmap_insert(ofport_map, &tnl_port->ofport_node, hash_pointer(ofport, 0));
 
123
    hmap_insert(tnl_match_map, &tnl_port->match_node,
112
124
                tnl_hash(&tnl_port->match));
113
125
    tnl_port_mod_log(tnl_port, "adding");
114
 
    return tnl_port;
 
126
    return true;
115
127
}
116
128
 
117
129
/* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's
118
130
 * must be added before they can be used by the module. 'ofport' must be a
119
131
 * tunnel. */
120
 
struct tnl_port *
121
 
tnl_port_add(const struct ofport *ofport, uint32_t odp_port)
 
132
void
 
133
tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev,
 
134
             odp_port_t odp_port) OVS_EXCLUDED(rwlock)
122
135
{
123
 
    return tnl_port_add__(ofport, odp_port, true);
 
136
    ovs_rwlock_wrlock(&rwlock);
 
137
    tnl_port_add__(ofport, netdev, odp_port, true);
 
138
    ovs_rwlock_unlock(&rwlock);
124
139
}
125
140
 
126
 
/* Checks if the tnl_port pointed to by 'tnl_portp' needs reconfiguration due
127
 
 * to changes in its netdev_tunnel_config.  If it does, updates 'tnl_portp' to
128
 
 * point to a new tnl_port and returns true.  Otherwise, returns false.
129
 
 * 'ofport' and 'odp_port' should be the same as would be passed to
 
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
130
144
 * tnl_port_add(). */
131
145
bool
132
 
tnl_port_reconfigure(const struct ofport *ofport, uint32_t odp_port,
133
 
                     struct tnl_port **tnl_portp)
 
146
tnl_port_reconfigure(const struct ofport_dpif *ofport,
 
147
                     const struct netdev *netdev, odp_port_t odp_port)
 
148
    OVS_EXCLUDED(rwlock)
134
149
{
135
 
    struct tnl_port *tnl_port = *tnl_portp;
 
150
    struct tnl_port *tnl_port;
 
151
    bool changed = false;
136
152
 
137
 
    if (tnl_port == &void_tnl_port) {
138
 
        *tnl_portp = tnl_port_add__(ofport, odp_port, false);
139
 
        return *tnl_portp != &void_tnl_port;
140
 
    } else if (tnl_port->ofport != ofport
 
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
141
158
               || tnl_port->match.odp_port != odp_port
142
 
               || tnl_port->netdev_seq != netdev_change_seq(ofport->netdev)) {
 
159
               || tnl_port->netdev_seq != netdev_change_seq(netdev)) {
143
160
        VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port));
144
 
        tnl_port_del(tnl_port);
145
 
        *tnl_portp = tnl_port_add(ofport, odp_port);
146
 
        return true;
 
161
        tnl_port_del__(ofport);
 
162
        tnl_port_add__(ofport, netdev, odp_port, true);
 
163
        changed = true;
147
164
    }
148
 
    return false;
 
165
    ovs_rwlock_unlock(&rwlock);
 
166
    return changed;
149
167
}
150
168
 
151
 
/* Removes 'tnl_port' from the module. */
152
 
void
153
 
tnl_port_del(struct tnl_port *tnl_port)
 
169
static void
 
170
tnl_port_del__(const struct ofport_dpif *ofport) OVS_REQ_WRLOCK(rwlock)
154
171
{
155
 
    if (tnl_port && tnl_port != &void_tnl_port) {
 
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) {
156
180
        tnl_port_mod_log(tnl_port, "removing");
157
 
        hmap_remove(&tnl_match_map, &tnl_port->match_node);
 
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);
158
184
        free(tnl_port);
159
185
    }
160
186
}
161
187
 
162
 
/* Transforms 'flow' so that it appears to have been received by a tunnel
163
 
 * OpenFlow port controlled by this module instead of the datapath port it
164
 
 * actually came in on.  Sets 'flow''s in_port to the appropriate OpenFlow port
165
 
 * number.  Returns the 'ofport' corresponding to the new in_port.
 
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.
166
200
 *
167
201
 * Callers should verify that 'flow' needs to be received by calling
168
 
 * tnl_port_should_receive() before this function.
169
 
 *
170
 
 * Leaves 'flow' untouched and returns null if unsuccessful. */
171
 
const struct ofport *
172
 
tnl_port_receive(struct flow *flow)
 
202
 * tnl_port_should_receive() before this function. */
 
203
const struct ofport_dpif *
 
204
tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock)
173
205
{
174
206
    char *pre_flow_str = NULL;
 
207
    const struct ofport_dpif *ofport;
175
208
    struct tnl_port *tnl_port;
176
209
    struct tnl_match match;
177
210
 
178
211
    memset(&match, 0, sizeof match);
179
 
    match.odp_port = flow->in_port;
 
212
    match.odp_port = flow->in_port.odp_port;
180
213
    match.ip_src = flow->tunnel.ip_dst;
181
214
    match.ip_dst = flow->tunnel.ip_src;
182
215
    match.in_key = flow->tunnel.tun_id;
183
216
    match.skb_mark = flow->skb_mark;
184
217
 
 
218
    ovs_rwlock_rdlock(&rwlock);
185
219
    tnl_port = tnl_find(&match);
 
220
    ofport = tnl_port ? tnl_port->ofport : NULL;
186
221
    if (!tnl_port) {
187
222
        struct ds ds = DS_EMPTY_INITIALIZER;
188
223
 
189
224
        tnl_match_fmt(&match, &ds);
190
225
        VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", ds_cstr(&ds));
191
226
        ds_destroy(&ds);
192
 
        return NULL;
 
227
        goto out;
193
228
    }
194
229
 
195
230
    if (!VLOG_DROP_DBG(&dbg_rl)) {
196
231
        pre_flow_str = flow_to_string(flow);
197
232
    }
198
233
 
199
 
    flow->in_port = tnl_port->ofport->ofp_port;
200
 
    memset(&flow->tunnel, 0, sizeof flow->tunnel);
201
 
    flow->tunnel.tun_id = match.in_key;
202
 
 
203
234
    if (pre_flow_str) {
204
235
        char *post_flow_str = flow_to_string(flow);
205
236
        char *tnl_str = tnl_port_fmt(tnl_port);
212
243
        free(pre_flow_str);
213
244
        free(post_flow_str);
214
245
    }
215
 
    return tnl_port->ofport;
 
246
 
 
247
out:
 
248
    ovs_rwlock_unlock(&rwlock);
 
249
    return ofport;
216
250
}
217
251
 
218
252
/* Given that 'flow' should be output to the ofport corresponding to
219
253
 * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath
220
 
 * port that the output should happen on.  May return OVSP_NONE if the output
 
254
 * port that the output should happen on.  May return ODPP_NONE if the output
221
255
 * shouldn't occur. */
222
 
uint32_t
223
 
tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow)
 
256
odp_port_t
 
257
tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
 
258
              struct flow_wildcards *wc) OVS_EXCLUDED(rwlock)
224
259
{
225
260
    const struct netdev_tunnel_config *cfg;
 
261
    struct tnl_port *tnl_port;
226
262
    char *pre_flow_str = NULL;
 
263
    odp_port_t out_port;
227
264
 
228
 
    if (tnl_port == &void_tnl_port) {
229
 
        return OVSP_NONE;
 
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;
230
270
    }
231
271
 
232
 
    cfg = netdev_get_tunnel_config(tnl_port->ofport->netdev);
 
272
    cfg = netdev_get_tunnel_config(tnl_port->netdev);
233
273
    ovs_assert(cfg);
234
274
 
235
275
    if (!VLOG_DROP_DBG(&dbg_rl)) {
236
276
        pre_flow_str = flow_to_string(flow);
237
277
    }
238
278
 
239
 
    flow->tunnel.ip_src = tnl_port->match.ip_src;
240
 
    flow->tunnel.ip_dst = tnl_port->match.ip_dst;
 
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
    }
241
285
    flow->skb_mark = tnl_port->match.skb_mark;
242
286
 
243
287
    if (!cfg->out_key_flow) {
245
289
    }
246
290
 
247
291
    if (cfg->ttl_inherit && is_ip_any(flow)) {
 
292
        wc->masks.nw_ttl = 0xff;
248
293
        flow->tunnel.ip_ttl = flow->nw_ttl;
249
294
    } else {
250
295
        flow->tunnel.ip_ttl = cfg->ttl;
251
296
    }
252
297
 
253
298
    if (cfg->tos_inherit && is_ip_any(flow)) {
 
299
        wc->masks.nw_tos = 0xff;
254
300
        flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK;
255
301
    } else {
256
302
        flow->tunnel.ip_tos = cfg->tos;
257
303
    }
258
304
 
 
305
    /* ECN fields are always inherited. */
 
306
    if (is_ip_any(flow)) {
 
307
        wc->masks.nw_tos |= IP_ECN_MASK;
 
308
    }
 
309
 
259
310
    if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) {
260
311
        flow->tunnel.ip_tos |= IP_ECN_ECT_0;
261
312
    } else {
279
330
        free(post_flow_str);
280
331
    }
281
332
 
282
 
    return tnl_port->match.odp_port;
 
333
out:
 
334
    ovs_rwlock_unlock(&rwlock);
 
335
    return out_port;
283
336
}
284
337
 
285
338
static uint32_t
290
343
}
291
344
 
292
345
static struct tnl_port *
293
 
tnl_find_exact(struct tnl_match *match)
 
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)
294
361
{
295
362
    struct tnl_port *tnl_port;
296
363
 
297
364
    HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match),
298
 
                             &tnl_match_map) {
 
365
                             tnl_match_map) {
299
366
        if (!memcmp(match, &tnl_port->match, sizeof *match)) {
300
367
            return tnl_port;
301
368
        }
304
371
}
305
372
 
306
373
static struct tnl_port *
307
 
tnl_find(struct tnl_match *match_)
 
374
tnl_find(struct tnl_match *match_) OVS_REQ_RDLOCK(rwlock)
308
375
{
309
376
    struct tnl_match match = *match_;
310
377
    struct tnl_port *tnl_port;
338
405
        return tnl_port;
339
406
    }
340
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
 
341
424
    return NULL;
342
425
}
343
426
 
344
427
static void
345
428
tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
 
429
    OVS_REQ_RDLOCK(rwlock)
346
430
{
347
 
    ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
348
 
                  IP_ARGS(match->ip_dst));
 
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
    }
349
439
 
350
440
    if (match->in_key_flow) {
351
441
        ds_put_cstr(ds, ", key=flow");
359
449
 
360
450
static void
361
451
tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action)
 
452
    OVS_REQ_RDLOCK(rwlock)
362
453
{
363
454
    if (VLOG_IS_DBG_ENABLED()) {
364
455
        struct ds ds = DS_EMPTY_INITIALIZER;
371
462
}
372
463
 
373
464
static char *
374
 
tnl_port_fmt(const struct tnl_port *tnl_port)
 
465
tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
375
466
{
376
467
    const struct netdev_tunnel_config *cfg =
377
 
        netdev_get_tunnel_config(tnl_port->ofport->netdev);
 
468
        netdev_get_tunnel_config(tnl_port->netdev);
378
469
    struct ds ds = DS_EMPTY_INITIALIZER;
379
470
 
380
471
    ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port,
381
472
                  tnl_port_get_name(tnl_port),
382
 
                  netdev_get_type(tnl_port->ofport->netdev));
 
473
                  netdev_get_type(tnl_port->netdev));
383
474
    tnl_match_fmt(&tnl_port->match, &ds);
384
475
 
385
476
    if (cfg->out_key != cfg->in_key ||
421
512
}
422
513
 
423
514
static const char *
424
 
tnl_port_get_name(const struct tnl_port *tnl_port)
 
515
tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
425
516
{
426
 
    return netdev_get_name(tnl_port->ofport->netdev);
 
517
    return netdev_get_name(tnl_port->netdev);
427
518
}