Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/tun-metadata.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2015 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
#include <errno.h>
19
#include <stdbool.h>
20
21
#include "bitmap.h"
22
#include "compiler.h"
23
#include "openvswitch/hmap.h"
24
#include "openvswitch/match.h"
25
#include "nx-match.h"
26
#include "odp-netlink.h"
27
#include "openvswitch/ofp-match.h"
28
#include "ovs-rcu.h"
29
#include "packets.h"
30
#include "tun-metadata.h"
31
#include "util.h"
32
33
struct tun_meta_entry {
34
    struct hmap_node node;      /* In struct tun_table's key_hmap. */
35
    struct tun_metadata_loc loc;
36
    uint32_t key;               /* (class << 8) | type. */
37
    bool valid;                 /* True if allocated to a class and type. */
38
};
39
40
/* Maps from TLV option class+type to positions in a struct tun_metadata's
41
 * 'opts' array.  */
42
struct tun_table {
43
    /* TUN_METADATA<i> is stored in element <i>. */
44
    struct tun_meta_entry entries[TUN_METADATA_NUM_OPTS];
45
46
    /* Each bit represents 4 bytes of space, 0-bits are free space. */
47
    unsigned long alloc_map[BITMAP_N_LONGS(TUN_METADATA_TOT_OPT_SIZE / 4)];
48
49
    /* The valid elements in entries[], indexed by class+type. */
50
    struct hmap key_hmap;
51
};
52
BUILD_ASSERT_DECL(TUN_METADATA_TOT_OPT_SIZE % 4 == 0);
53
54
static enum ofperr tun_metadata_add_entry(struct tun_table *map, uint8_t idx,
55
                                          uint16_t opt_class, uint8_t type,
56
                                          uint8_t len);
57
static void tun_metadata_del_entry(struct tun_table *map, uint8_t idx);
58
static void memcpy_to_metadata(struct tun_metadata *dst, const void *src,
59
                               const struct tun_metadata_loc *,
60
                               unsigned int idx);
61
static void memcpy_from_metadata(void *dst, const struct tun_metadata *src,
62
                                 const struct tun_metadata_loc *);
63
64
static uint32_t
65
tun_meta_key(ovs_be16 class, uint8_t type)
66
0
{
67
0
    return (OVS_FORCE uint16_t)class << 8 | type;
68
0
}
69
70
static ovs_be16
71
tun_key_class(uint32_t key)
72
0
{
73
0
    return (OVS_FORCE ovs_be16)(key >> 8);
74
0
}
75
76
static uint8_t
77
tun_key_type(uint32_t key)
78
0
{
79
0
    return key & 0xff;
80
0
}
81
82
/* Returns a newly allocated tun_table.  If 'old_map' is nonnull then the new
83
 * tun_table is a deep copy of the old one. */
84
struct tun_table *
85
tun_metadata_alloc(const struct tun_table *old_map)
86
0
{
87
0
    struct tun_table *new_map;
88
89
0
    new_map = xzalloc(sizeof *new_map);
90
91
0
    if (old_map) {
92
0
        struct tun_meta_entry *entry;
93
94
0
        *new_map = *old_map;
95
0
        hmap_init(&new_map->key_hmap);
96
97
0
        HMAP_FOR_EACH (entry, node, &old_map->key_hmap) {
98
0
            struct tun_meta_entry *new_entry;
99
0
            struct tun_metadata_loc_chain *chain;
100
101
0
            new_entry = &new_map->entries[entry - old_map->entries];
102
0
            hmap_insert(&new_map->key_hmap, &new_entry->node, entry->node.hash);
103
104
0
            chain = &new_entry->loc.c;
105
0
            while (chain->next) {
106
0
                chain->next = xmemdup(chain->next, sizeof *chain->next);
107
0
                chain = chain->next;
108
0
            }
109
0
        }
110
0
    } else {
111
0
        hmap_init(&new_map->key_hmap);
112
0
    }
113
114
0
    return new_map;
115
0
}
116
117
/* Frees 'map' and all the memory it owns. */
118
void
119
tun_metadata_free(struct tun_table *map)
120
0
{
121
0
    struct tun_meta_entry *entry;
122
123
0
    if (!map) {
124
0
        return;
125
0
    }
126
127
0
    HMAP_FOR_EACH (entry, node, &map->key_hmap) {
128
0
        tun_metadata_del_entry(map, entry - map->entries);
129
0
    }
130
131
0
    hmap_destroy(&map->key_hmap);
132
0
    free(map);
133
0
}
134
135
void
136
tun_metadata_postpone_free(struct tun_table *tab)
137
0
{
138
0
    ovsrcu_postpone(tun_metadata_free, tab);
139
0
}
140
141
enum ofperr
142
tun_metadata_table_mod(struct ofputil_tlv_table_mod *ttm,
143
                       const struct tun_table *old_tab,
144
                       struct tun_table **new_tab)
145
0
{
146
0
    struct ofputil_tlv_map *ofp_map;
147
0
    enum ofperr err = 0;
148
149
0
    switch (ttm->command) {
150
0
    case NXTTMC_ADD:
151
0
        *new_tab = tun_metadata_alloc(old_tab);
152
153
0
        LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) {
154
0
            err = tun_metadata_add_entry(*new_tab, ofp_map->index,
155
0
                                         ofp_map->option_class,
156
0
                                         ofp_map->option_type,
157
0
                                         ofp_map->option_len);
158
0
            if (err) {
159
0
                tun_metadata_free(*new_tab);
160
0
                *new_tab = NULL;
161
0
                return err;
162
0
            }
163
0
        }
164
0
        break;
165
166
0
    case NXTTMC_DELETE:
167
0
        *new_tab = tun_metadata_alloc(old_tab);
168
169
0
        LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) {
170
0
            tun_metadata_del_entry(*new_tab, ofp_map->index);
171
0
        }
172
0
        break;
173
174
0
    case NXTTMC_CLEAR:
175
0
        *new_tab = tun_metadata_alloc(NULL);
176
0
        break;
177
178
0
    default:
179
0
        OVS_NOT_REACHED();
180
0
    }
181
182
0
    return 0;
183
0
}
184
185
void
186
tun_metadata_table_request(const struct tun_table *tun_table,
187
                           struct ofputil_tlv_table_reply *ttr)
188
0
{
189
0
    int i;
190
191
0
    ttr->max_option_space = TUN_METADATA_TOT_OPT_SIZE;
192
0
    ttr->max_fields = TUN_METADATA_NUM_OPTS;
193
0
    ovs_list_init(&ttr->mappings);
194
195
0
    for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) {
196
0
        const struct tun_meta_entry *entry = &tun_table->entries[i];
197
0
        struct ofputil_tlv_map *map;
198
199
0
        if (!entry->valid) {
200
0
            continue;
201
0
        }
202
203
0
        map = xmalloc(sizeof *map);
204
0
        map->option_class = ntohs(tun_key_class(entry->key));
205
0
        map->option_type = tun_key_type(entry->key);
206
0
        map->option_len = entry->loc.len;
207
0
        map->index = i;
208
209
0
        ovs_list_push_back(&ttr->mappings, &map->list_node);
210
0
    }
211
0
}
212
213
/* Copies the value of field 'mf' from 'tnl' (which must be in non-UDPIF format) * into 'value'.
214
 *
215
 * 'mf' must be an MFF_TUN_METADATA* field.
216
 *
217
 * This uses the tunnel metadata mapping table created by tun_metadata_alloc().
218
 * If no such table has been created or if 'mf' hasn't been allocated in it yet,
219
 * this just zeros 'value'. */
220
void
221
tun_metadata_read(const struct flow_tnl *tnl,
222
                  const struct mf_field *mf, union mf_value *value)
223
0
{
224
0
    const struct tun_table *map = tnl->metadata.tab;
225
0
    unsigned int idx = mf->id - MFF_TUN_METADATA0;
226
0
    const struct tun_metadata_loc *loc;
227
228
0
    if (!map) {
229
0
        memset(value->tun_metadata, 0, mf->n_bytes);
230
0
        return;
231
0
    }
232
233
0
    loc = &map->entries[idx].loc;
234
235
0
    memset(value->tun_metadata, 0, mf->n_bytes - loc->len);
236
0
    memcpy_from_metadata(value->tun_metadata + mf->n_bytes - loc->len,
237
0
                         &tnl->metadata, loc);
238
0
}
239
240
/* Copies 'value' into field 'mf' in 'tnl' (in non-UDPIF format).
241
 *
242
 * 'mf' must be an MFF_TUN_METADATA* field.
243
 *
244
 * This uses the tunnel metadata mapping table created by tun_metadata_alloc().
245
 * If no such table has been created or if 'mf' hasn't been allocated in it yet,
246
 * this function does nothing. */
247
void
248
tun_metadata_write(struct flow_tnl *tnl,
249
                   const struct mf_field *mf, const union mf_value *value)
250
0
{
251
0
    const struct tun_table *map = tnl->metadata.tab;
252
0
    unsigned int idx = mf->id - MFF_TUN_METADATA0;
253
0
    const struct tun_metadata_loc *loc;
254
255
0
    if (!map || !map->entries[idx].valid) {
256
0
        return;
257
0
    }
258
259
0
    loc = &map->entries[idx].loc;
260
0
    memcpy_to_metadata(&tnl->metadata,
261
0
                       value->tun_metadata + mf->n_bytes - loc->len, loc, idx);
262
0
}
263
264
/* Deletes field 'mf' in 'tnl' (in non-UDPIF format).
265
 * 'mf' must be an MFF_TUN_METADATA* field.
266
 */
267
void
268
tun_metadata_delete(struct flow_tnl *tnl, const struct mf_field *mf)
269
0
{
270
0
    unsigned int idx;
271
272
0
    if (tnl->flags & FLOW_TNL_F_UDPIF) {
273
0
        return;
274
0
    }
275
276
0
    idx = mf->id - MFF_TUN_METADATA0;
277
0
    ovs_assert(idx < TUN_METADATA_NUM_OPTS);
278
0
    ULLONG_SET0(tnl->metadata.present.map, idx);
279
0
}
280
281
static const struct tun_metadata_loc *
282
metadata_loc_from_match(const struct tun_table *map, struct match *match,
283
                        const char *name, unsigned int idx,
284
                        unsigned int field_len, bool masked, char **err_str)
285
0
{
286
0
    ovs_assert(idx < TUN_METADATA_NUM_OPTS);
287
288
0
    if (err_str) {
289
0
        *err_str = NULL;
290
0
    }
291
292
0
    if (map) {
293
0
        if (map->entries[idx].valid) {
294
0
            return &map->entries[idx].loc;
295
0
        } else {
296
0
            return NULL;
297
0
        }
298
0
    }
299
300
0
    if (match->tun_md.alloc_offset + field_len > TUN_METADATA_TOT_OPT_SIZE) {
301
0
        if (err_str) {
302
0
            *err_str = xasprintf("field %s exceeds maximum size for tunnel "
303
0
                                 "metadata (used %d, max %d)", name,
304
0
                                 match->tun_md.alloc_offset + field_len,
305
0
                                 TUN_METADATA_TOT_OPT_SIZE);
306
0
        }
307
308
0
        return NULL;
309
0
    }
310
311
0
    if (ULLONG_GET(match->wc.masks.tunnel.metadata.present.map, idx)) {
312
0
        if (err_str) {
313
0
            *err_str = xasprintf("field %s set multiple times", name);
314
0
        }
315
316
0
        return NULL;
317
0
    }
318
319
0
    match->tun_md.entry[idx].loc.len = field_len;
320
0
    match->tun_md.entry[idx].loc.c.offset = match->tun_md.alloc_offset;
321
0
    match->tun_md.entry[idx].loc.c.len = field_len;
322
0
    match->tun_md.entry[idx].loc.c.next = NULL;
323
0
    match->tun_md.entry[idx].masked = masked;
324
0
    match->tun_md.alloc_offset += field_len;
325
0
    match->tun_md.valid = true;
326
327
0
    return &match->tun_md.entry[idx].loc;
328
0
}
329
330
/* Makes 'match' match 'value'/'mask' on field 'mf'.
331
 *
332
 * 'mf' must be an MFF_TUN_METADATA* field. 'match' must be in non-UDPIF format.
333
 *
334
 * If there is a tunnel metadata mapping table associated with the switch,
335
 * this function is effective only if there is already a mapping for 'mf'.
336
 * Otherwise, the metadata mapping table integrated into 'match' is used,
337
 * adding 'mf' to its mapping table if it isn't already mapped (and if there
338
 * is room).  If 'mf' isn't or can't be mapped, this function returns without
339
 * modifying 'match'.
340
 *
341
 * 'value' may be NULL; if so, then 'mf' is made to match on an all-zeros
342
 * value.
343
 *
344
 * 'mask' may be NULL; if so, then 'mf' is made exact-match.
345
 *
346
 * If non-NULL, 'err_str' returns a malloc'ed string describing any errors
347
 * with the request or NULL if there is no error. The caller is reponsible
348
 * for freeing the string.
349
 */
350
void
351
tun_metadata_set_match(const struct mf_field *mf, const union mf_value *value,
352
                       const union mf_value *mask, struct match *match,
353
                       char **err_str)
354
0
{
355
0
    const struct tun_table *map = match->flow.tunnel.metadata.tab;
356
0
    const struct tun_metadata_loc *loc;
357
0
    unsigned int idx = mf->id - MFF_TUN_METADATA0;
358
0
    unsigned int field_len;
359
0
    bool is_masked;
360
0
    unsigned int data_offset;
361
0
    union mf_value data;
362
363
0
    field_len = mf_field_len(mf, value, mask, &is_masked);
364
0
    loc = metadata_loc_from_match(map, match, mf->name, idx, field_len,
365
0
                                  is_masked, err_str);
366
0
    if (!loc) {
367
0
        return;
368
0
    }
369
370
0
    data_offset = mf->n_bytes - loc->len;
371
372
0
    if (!value) {
373
0
        memset(data.tun_metadata, 0, loc->len);
374
0
    } else if (!mask) {
375
0
        memcpy(data.tun_metadata, value->tun_metadata + data_offset, loc->len);
376
0
    } else {
377
0
        int i;
378
0
        for (i = 0; i < loc->len; i++) {
379
0
            data.tun_metadata[i] = value->tun_metadata[data_offset + i] &
380
0
                                   mask->tun_metadata[data_offset + i];
381
0
        }
382
0
    }
383
0
    memcpy_to_metadata(&match->flow.tunnel.metadata, data.tun_metadata,
384
0
                       loc, idx);
385
386
0
    if (!value) {
387
0
        memset(data.tun_metadata, 0, loc->len);
388
0
    } else if (!mask) {
389
0
        memset(data.tun_metadata, 0xff, loc->len);
390
0
    } else {
391
0
        memcpy(data.tun_metadata, mask->tun_metadata + data_offset, loc->len);
392
0
    }
393
0
    memcpy_to_metadata(&match->wc.masks.tunnel.metadata, data.tun_metadata,
394
0
                       loc, idx);
395
0
}
396
397
/* Copies all MFF_TUN_METADATA* fields from 'tnl' to 'flow_metadata'. This
398
 * is called during action translation and therefore 'tnl' must be in
399
 * non-udpif format. */
400
void
401
tun_metadata_get_fmd(const struct flow_tnl *tnl, struct match *flow_metadata)
402
0
{
403
0
    int i;
404
405
0
    ULLONG_FOR_EACH_1 (i, tnl->metadata.present.map) {
406
0
        union mf_value opts;
407
0
        const struct tun_metadata_loc *old_loc = &tnl->metadata.tab->entries[i].loc;
408
0
        const struct tun_metadata_loc *new_loc;
409
410
0
        new_loc = metadata_loc_from_match(NULL, flow_metadata, NULL, i,
411
0
                                          old_loc->len, false, NULL);
412
413
0
        memcpy_from_metadata(opts.tun_metadata, &tnl->metadata, old_loc);
414
0
        memcpy_to_metadata(&flow_metadata->flow.tunnel.metadata,
415
0
                           opts.tun_metadata, new_loc, i);
416
417
0
        memset(opts.tun_metadata, 0xff, old_loc->len);
418
0
        memcpy_to_metadata(&flow_metadata->wc.masks.tunnel.metadata,
419
0
                           opts.tun_metadata, new_loc, i);
420
0
    }
421
0
}
422
423
static uint32_t
424
tun_meta_hash(uint32_t key)
425
0
{
426
0
    return hash_int(key, 0);
427
0
}
428
429
static struct tun_meta_entry *
430
tun_meta_find_key(const struct hmap *hmap, uint32_t key)
431
0
{
432
0
    struct tun_meta_entry *entry;
433
434
0
    HMAP_FOR_EACH_IN_BUCKET (entry, node, tun_meta_hash(key), hmap) {
435
0
        if (entry->key == key) {
436
0
            return entry;
437
0
        }
438
0
    }
439
0
    return NULL;
440
0
}
441
442
static void
443
memcpy_to_metadata(struct tun_metadata *dst, const void *src,
444
                   const struct tun_metadata_loc *loc, unsigned int idx)
445
0
{
446
0
    const struct tun_metadata_loc_chain *chain = &loc->c;
447
0
    int addr = 0;
448
449
0
    while (chain) {
450
0
        memcpy(dst->opts.u8 + chain->offset, (uint8_t *)src + addr,
451
0
               chain->len);
452
0
        addr += chain->len;
453
0
        chain = chain->next;
454
0
    }
455
456
0
    ULLONG_SET1(dst->present.map, idx);
457
0
}
458
459
static void
460
memcpy_from_metadata(void *dst, const struct tun_metadata *src,
461
                     const struct tun_metadata_loc *loc)
462
0
{
463
0
    const struct tun_metadata_loc_chain *chain = &loc->c;
464
0
    int addr = 0;
465
466
0
    while (chain) {
467
0
        memcpy((uint8_t *)dst + addr, src->opts.u8 + chain->offset,
468
0
               chain->len);
469
0
        addr += chain->len;
470
0
        chain = chain->next;
471
0
    }
472
0
}
473
474
static int
475
tun_metadata_alloc_chain(struct tun_table *map, uint8_t len,
476
                         struct tun_metadata_loc_chain *loc)
477
0
{
478
0
    int alloc_len = len / 4;
479
0
    int scan_start = 0;
480
0
    int scan_end = TUN_METADATA_TOT_OPT_SIZE / 4;
481
0
    int pos_start, pos_end, pos_len;
482
0
    int best_start = 0, best_len = 0;
483
484
0
    while (true) {
485
0
        pos_start = bitmap_scan(map->alloc_map, 0, scan_start, scan_end);
486
0
        if (pos_start == scan_end) {
487
0
            break;
488
0
        }
489
490
0
        pos_end = bitmap_scan(map->alloc_map, 1, pos_start,
491
0
                              MIN(pos_start + alloc_len, scan_end));
492
0
        pos_len = pos_end - pos_start;
493
0
        if (pos_len == alloc_len) {
494
0
            goto found;
495
0
        }
496
497
0
        if (pos_len > best_len) {
498
0
            best_start = pos_start;
499
0
            best_len = pos_len;
500
0
        }
501
0
        scan_start = pos_end + 1;
502
0
    }
503
504
0
    if (best_len == 0) {
505
0
        return ENOSPC;
506
0
    }
507
508
0
    pos_start = best_start;
509
0
    pos_len = best_len;
510
511
0
found:
512
0
    bitmap_set_multiple(map->alloc_map, pos_start, pos_len, 1);
513
0
    loc->offset = pos_start * 4;
514
0
    loc->len = pos_len * 4;
515
516
0
    return 0;
517
0
}
518
519
static enum ofperr
520
tun_metadata_add_entry(struct tun_table *map, uint8_t idx, uint16_t opt_class,
521
                       uint8_t type, uint8_t len)
522
0
{
523
0
    struct tun_meta_entry *entry;
524
0
    struct tun_metadata_loc_chain *cur_chain, *prev_chain;
525
526
0
    ovs_assert(idx < TUN_METADATA_NUM_OPTS);
527
528
0
    entry = &map->entries[idx];
529
0
    if (entry->valid) {
530
0
        return OFPERR_NXTTMFC_ALREADY_MAPPED;
531
0
    }
532
533
0
    entry->key = tun_meta_key(htons(opt_class), type);
534
0
    if (tun_meta_find_key(&map->key_hmap, entry->key)) {
535
0
        return OFPERR_NXTTMFC_DUP_ENTRY;
536
0
    }
537
538
0
    entry->valid = true;
539
0
    hmap_insert(&map->key_hmap, &entry->node,
540
0
                tun_meta_hash(entry->key));
541
542
0
    entry->loc.len = len;
543
0
    cur_chain = &entry->loc.c;
544
0
    memset(cur_chain, 0, sizeof *cur_chain);
545
0
    prev_chain = NULL;
546
547
0
    while (len) {
548
0
        int err;
549
550
0
        if (!cur_chain) {
551
0
            cur_chain = xzalloc(sizeof *cur_chain);
552
0
            prev_chain->next = cur_chain;
553
0
        }
554
555
0
        err = tun_metadata_alloc_chain(map, len, cur_chain);
556
0
        if (err) {
557
0
            tun_metadata_del_entry(map, idx);
558
0
            return OFPERR_NXTTMFC_TABLE_FULL;
559
0
        }
560
561
0
        len -= cur_chain->len;
562
563
0
        prev_chain = cur_chain;
564
0
        cur_chain = NULL;
565
0
    }
566
567
0
    return 0;
568
0
}
569
570
static void
571
tun_metadata_del_entry(struct tun_table *map, uint8_t idx)
572
0
{
573
0
    struct tun_meta_entry *entry;
574
0
    struct tun_metadata_loc_chain *chain;
575
576
0
    if (idx >= TUN_METADATA_NUM_OPTS) {
577
0
        return;
578
0
    }
579
580
0
    entry = &map->entries[idx];
581
0
    if (!entry->valid) {
582
0
        return;
583
0
    }
584
585
0
    chain = &entry->loc.c;
586
0
    while (chain) {
587
0
        struct tun_metadata_loc_chain *next = chain->next;
588
589
0
        bitmap_set_multiple(map->alloc_map, chain->offset / 4,
590
0
                            chain->len / 4, 0);
591
0
        if (chain != &entry->loc.c) {
592
0
            free(chain);
593
0
        }
594
0
        chain = next;
595
0
    }
596
597
0
    entry->valid = false;
598
0
    hmap_remove(&map->key_hmap, &entry->node);
599
0
    memset(&entry->loc, 0, sizeof entry->loc);
600
0
}
601
602
/* Converts from Geneve netlink attributes in 'attr' to tunnel metadata
603
 * in 'tun'. In reality, there is very little conversion done since we are
604
 * just copying over the tunnel options in the form that they were received
605
 * on the wire. By always using UDPIF format, this allows us to process the
606
 * flow key without any knowledge of the mapping table. We can do the
607
 * conversion later if necessary. */
608
void
609
tun_metadata_from_geneve_nlattr(const struct nlattr *attr, bool is_mask,
610
                                struct flow_tnl *tun)
611
0
{
612
0
    int attr_len = nl_attr_get_size(attr);
613
614
0
    memcpy(tun->metadata.opts.gnv, nl_attr_get(attr), attr_len);
615
0
    tun->flags |= FLOW_TNL_F_UDPIF;
616
617
0
    if (!is_mask) {
618
0
        tun->metadata.present.len = attr_len;
619
0
    } else {
620
        /* We need to exact match on the length so we don't
621
         * accidentally match on sets of options that are the same
622
         * at the beginning but with additional options after. */
623
0
        tun->metadata.present.len = 0xff;
624
0
    }
625
0
}
626
627
/* Converts from the flat Geneve options representation extracted directly
628
 * from the tunnel header to the representation that maps options to
629
 * pre-allocated locations. The original version (in UDPIF form) is passed
630
 * in 'src' and the translated form in stored in 'dst'.  To handle masks, the
631
 * flow must also be passed in through 'flow' (in the original, raw form). */
632
int
633
tun_metadata_from_geneve_udpif(const struct tun_table *tun_tab,
634
                               const struct flow_tnl *flow,
635
                               const struct flow_tnl *src,
636
                               struct flow_tnl *dst)
637
0
{
638
0
    const struct geneve_opt *opt = src->metadata.opts.gnv;
639
0
    const struct geneve_opt *flow_opt = flow->metadata.opts.gnv;
640
0
    int opts_len = flow->metadata.present.len;
641
642
0
    dst->metadata.tab = tun_tab;
643
0
    dst->flags = src->flags & ~FLOW_TNL_F_UDPIF;
644
0
    dst->metadata.present.map = 0;
645
646
0
    while (opts_len > 0) {
647
0
        int len;
648
0
        struct tun_meta_entry *entry;
649
650
0
        if (opts_len < sizeof(*opt)) {
651
0
            return EINVAL;
652
0
        }
653
654
0
        len = sizeof(*opt) + flow_opt->length * 4;
655
0
        if (len > opts_len) {
656
0
            return EINVAL;
657
0
        }
658
659
0
        entry = tun_meta_find_key(&tun_tab->key_hmap,
660
0
                                  tun_meta_key(flow_opt->opt_class,
661
0
                                               flow_opt->type));
662
0
        if (entry) {
663
0
            if (entry->loc.len == flow_opt->length * 4) {
664
0
                memcpy_to_metadata(&dst->metadata, opt + 1, &entry->loc,
665
0
                                   entry - tun_tab->entries);
666
0
            } else {
667
0
                return EINVAL;
668
0
            }
669
0
        } else if (flow_opt->type & GENEVE_CRIT_OPT_TYPE) {
670
0
            return EINVAL;
671
0
        }
672
673
0
        opt = opt + len / sizeof(*opt);
674
0
        flow_opt = flow_opt + len / sizeof(*opt);
675
0
        opts_len -= len;
676
0
    }
677
678
0
    return 0;
679
0
}
680
681
static void
682
tun_metadata_to_geneve__(const struct tun_metadata *flow, struct ofpbuf *b,
683
                         bool *crit_opt)
684
0
{
685
0
    int i;
686
687
0
    *crit_opt = false;
688
689
0
    ULLONG_FOR_EACH_1 (i, flow->present.map) {
690
0
        const struct tun_meta_entry *entry = &flow->tab->entries[i];
691
0
        struct geneve_opt *opt;
692
693
0
        opt = ofpbuf_put_uninit(b, sizeof *opt + entry->loc.len);
694
695
0
        opt->opt_class = tun_key_class(entry->key);
696
0
        opt->type = tun_key_type(entry->key);
697
0
        opt->length = entry->loc.len / 4;
698
0
        opt->r1 = 0;
699
0
        opt->r2 = 0;
700
0
        opt->r3 = 0;
701
702
0
        memcpy_from_metadata(opt + 1, flow, &entry->loc);
703
0
        *crit_opt |= !!(opt->type & GENEVE_CRIT_OPT_TYPE);
704
0
    }
705
0
}
706
707
static void
708
tun_metadata_to_geneve_nlattr_flow(const struct flow_tnl *flow,
709
                                   struct ofpbuf *b)
710
0
{
711
0
    size_t nlattr_offset;
712
0
    bool crit_opt;
713
714
0
    if (!flow->metadata.present.map) {
715
0
        return;
716
0
    }
717
718
    /* For all intents and purposes, the Geneve options are nested
719
     * attributes even if this doesn't show up directly to netlink. It's
720
     * similar enough that we can use the same mechanism. */
721
0
    nlattr_offset = nl_msg_start_nested(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
722
723
0
    tun_metadata_to_geneve__(&flow->metadata, b, &crit_opt);
724
725
0
    nl_msg_end_nested(b, nlattr_offset);
726
0
}
727
728
/* Converts from processed tunnel metadata information (in non-udpif
729
 * format) in 'flow' to a stream of Geneve options suitable for
730
 * transmission in 'opts'. Additionally returns whether there were
731
 * any critical options in 'crit_opt' as well as the total length of
732
 * data. */
733
int
734
tun_metadata_to_geneve_header(const struct flow_tnl *flow,
735
                              struct geneve_opt *opts, bool *crit_opt)
736
0
{
737
0
    struct ofpbuf b;
738
739
0
    ofpbuf_use_stack(&b, opts, TLV_TOT_OPT_SIZE);
740
0
    tun_metadata_to_geneve__(&flow->metadata, &b, crit_opt);
741
742
0
    return b.size;
743
0
}
744
745
static void
746
tun_metadata_to_geneve_mask__(const struct tun_metadata *flow,
747
                              const struct tun_metadata *mask,
748
                              struct geneve_opt *opt, int opts_len)
749
0
{
750
    /* All of these options have already been validated, so no need
751
     * for sanity checking. */
752
0
    while (opts_len > 0) {
753
0
        struct tun_meta_entry *entry;
754
0
        int len = sizeof(*opt) + opt->length * 4;
755
756
0
        entry = tun_meta_find_key(&flow->tab->key_hmap,
757
0
                                  tun_meta_key(opt->opt_class, opt->type));
758
0
        if (entry) {
759
0
            memcpy_from_metadata(opt + 1, mask, &entry->loc);
760
0
        } else {
761
0
            memset(opt + 1, 0, opt->length * 4);
762
0
        }
763
764
0
        opt->opt_class = htons(0xffff);
765
0
        opt->type = 0xff;
766
0
        opt->length = 0x1f;
767
0
        opt->r1 = 0;
768
0
        opt->r2 = 0;
769
0
        opt->r3 = 0;
770
771
0
        opt = opt + len / sizeof(*opt);
772
0
        opts_len -= len;
773
0
    }
774
0
}
775
776
static void
777
tun_metadata_to_geneve_nlattr_mask(const struct ofpbuf *key,
778
                                   const struct flow_tnl *mask,
779
                                   const struct flow_tnl *flow,
780
                                   struct ofpbuf *b)
781
0
{
782
0
    const struct nlattr *tnl_key, *geneve_key;
783
0
    struct nlattr *geneve_mask;
784
0
    struct geneve_opt *opt;
785
0
    int opts_len;
786
787
0
    if (!key) {
788
0
        return;
789
0
    }
790
791
0
    tnl_key = nl_attr_find__(key->data, key->size, OVS_KEY_ATTR_TUNNEL);
792
0
    if (!tnl_key) {
793
0
        return;
794
0
    }
795
796
0
    geneve_key = nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
797
0
    if (!geneve_key) {
798
0
        return;
799
0
    }
800
801
0
    geneve_mask = ofpbuf_tail(b);
802
0
    nl_msg_put(b, geneve_key, geneve_key->nla_len);
803
804
0
    opt = CONST_CAST(struct geneve_opt *, nl_attr_get(geneve_mask));
805
0
    opts_len = nl_attr_get_size(geneve_mask);
806
807
0
    tun_metadata_to_geneve_mask__(&flow->metadata, &mask->metadata,
808
0
                                  opt, opts_len);
809
0
}
810
811
/* Convert from the tunnel metadata in 'tun' to netlink attributes stored
812
 * in 'b'. Either UDPIF or non-UDPIF input forms are accepted.
813
 *
814
 * To assist with parsing, it is necessary to also pass in the tunnel metadata
815
 * from the flow in 'flow' as well in the original netlink form of the flow in
816
 * 'key'. */
817
void
818
tun_metadata_to_geneve_nlattr(const struct flow_tnl *tun,
819
                              const struct flow_tnl *flow,
820
                              const struct ofpbuf *key,
821
                              struct ofpbuf *b)
822
0
{
823
0
    bool is_mask = tun != flow;
824
825
0
    if (!(flow->flags & FLOW_TNL_F_UDPIF)) {
826
0
        if (!is_mask) {
827
0
            tun_metadata_to_geneve_nlattr_flow(tun, b);
828
0
        } else {
829
0
            tun_metadata_to_geneve_nlattr_mask(key, tun, flow, b);
830
0
        }
831
0
    } else {
832
0
        nl_msg_put_unspec(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
833
0
                          tun->metadata.opts.gnv,
834
0
                          flow->metadata.present.len);
835
0
    }
836
0
}
837
838
/* Converts 'mask_src' (in non-UDPIF format) to a series of masked options in
839
 * 'dst'. 'flow_src' (also in non-UDPIF format) and the  original set of
840
 * options 'flow_src_opt'/'opts_len' are needed as a guide to interpret the
841
 * mask data. */
842
void
843
tun_metadata_to_geneve_udpif_mask(const struct flow_tnl *flow_src,
844
                                  const struct flow_tnl *mask_src,
845
                                  const struct geneve_opt *flow_src_opt,
846
                                  int opts_len, struct geneve_opt *dst)
847
0
{
848
0
    memcpy(dst, flow_src_opt, opts_len);
849
0
    tun_metadata_to_geneve_mask__(&flow_src->metadata,
850
0
                                  &mask_src->metadata, dst, opts_len);
851
0
}
852
853
static const struct tun_metadata_loc *
854
metadata_loc_from_match_read(const struct tun_table *map,
855
                             const struct match *match, unsigned int idx,
856
                             const struct flow_tnl *mask, bool *is_masked)
857
0
{
858
0
    union mf_value mask_opts;
859
860
0
    if (match->tun_md.valid) {
861
0
        *is_masked = match->tun_md.entry[idx].masked;
862
0
        return &match->tun_md.entry[idx].loc;
863
0
    }
864
865
0
    memcpy_from_metadata(mask_opts.tun_metadata, &mask->metadata,
866
0
                         &map->entries[idx].loc);
867
868
0
    *is_masked = map->entries[idx].loc.len == 0 ||
869
0
                 !is_all_ones(mask_opts.tun_metadata,
870
0
                              map->entries[idx].loc.len);
871
0
    return &map->entries[idx].loc;
872
0
}
873
874
/* Generates NXM formatted matches in 'b' based on the contents of 'match'.
875
 * 'match' must be in non-udpif format. */
876
void
877
tun_metadata_to_nx_match(struct ofpbuf *b, enum ofp_version oxm,
878
                         const struct match *match)
879
0
{
880
0
    int i;
881
882
0
    ULLONG_FOR_EACH_1 (i, match->wc.masks.tunnel.metadata.present.map) {
883
0
        const struct tun_metadata_loc *loc;
884
0
        bool is_masked;
885
0
        union mf_value opts;
886
0
        union mf_value mask_opts;
887
888
0
        loc = metadata_loc_from_match_read(match->flow.tunnel.metadata.tab,
889
0
                                           match, i, &match->wc.masks.tunnel,
890
0
                                           &is_masked);
891
0
        memcpy_from_metadata(opts.tun_metadata, &match->flow.tunnel.metadata,
892
0
                             loc);
893
0
        memcpy_from_metadata(mask_opts.tun_metadata,
894
0
                             &match->wc.masks.tunnel.metadata, loc);
895
0
        nxm_put_entry_raw(b, MFF_TUN_METADATA0 + i, oxm, opts.tun_metadata,
896
0
                          is_masked ? mask_opts.tun_metadata : NULL, loc->len);
897
0
    }
898
0
}
899
900
/* Formatted matches in 's' based on the contents of 'match'. 'match' must be
901
 * in non-udpif format. */
902
void
903
tun_metadata_match_format(struct ds *s, const struct match *match)
904
0
{
905
0
    int i;
906
907
0
    if (match->flow.tunnel.flags & FLOW_TNL_F_UDPIF ||
908
0
        (!match->flow.tunnel.metadata.tab && !match->tun_md.valid)) {
909
0
        return;
910
0
    }
911
912
0
    ULLONG_FOR_EACH_1 (i, match->wc.masks.tunnel.metadata.present.map) {
913
0
        const struct tun_metadata_loc *loc;
914
0
        bool is_masked;
915
0
        union mf_value opts, mask_opts;
916
917
0
        loc = metadata_loc_from_match_read(match->flow.tunnel.metadata.tab,
918
0
                                           match, i, &match->wc.masks.tunnel,
919
0
                                           &is_masked);
920
921
0
        ds_put_format(s, "tun_metadata%u", i);
922
0
        memcpy_from_metadata(mask_opts.tun_metadata,
923
0
                             &match->wc.masks.tunnel.metadata, loc);
924
925
0
        if (!ULLONG_GET(match->flow.tunnel.metadata.present.map, i)) {
926
            /* Indicate that we are matching on the field being not present. */
927
0
            ds_put_cstr(s, "=NP");
928
0
        } else if (!(is_masked &&
929
0
                     is_all_zeros(mask_opts.tun_metadata, loc->len))) {
930
0
            ds_put_char(s, '=');
931
932
0
            memcpy_from_metadata(opts.tun_metadata,
933
0
                                 &match->flow.tunnel.metadata, loc);
934
0
            ds_put_hex(s, opts.tun_metadata, loc->len);
935
936
0
            if (!is_all_ones(mask_opts.tun_metadata, loc->len)) {
937
0
                ds_put_char(s, '/');
938
0
                ds_put_hex(s, mask_opts.tun_metadata, loc->len);
939
0
            }
940
0
        }
941
0
        ds_put_char(s, ',');
942
0
    }
943
0
}
944
945
struct tun_metadata_allocation *
946
tun_metadata_allocation_clone(const struct tun_metadata_allocation *src)
947
0
{
948
0
    return src && src->valid ? xmemdup(src, sizeof *src) : NULL;
949
0
}
950
951
void
952
tun_metadata_allocation_copy(struct tun_metadata_allocation *dst,
953
                             const struct tun_metadata_allocation *src)
954
0
{
955
0
    if (src && src->valid) {
956
0
        *dst = *src;
957
0
    } else {
958
0
        memset(dst, 0, sizeof *dst);
959
0
    }
960
0
}