Coverage Report

Created: 2025-07-01 06:51

/src/openvswitch/lib/tnl-ports.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014, 2015, 2017 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 "tnl-ports.h"
20
21
#include <stddef.h>
22
#include <stdint.h>
23
#include <string.h>
24
25
#include "classifier.h"
26
#include "openvswitch/dynamic-string.h"
27
#include "hash.h"
28
#include "openvswitch/list.h"
29
#include "netdev.h"
30
#include "openvswitch/ofpbuf.h"
31
#include "ovs-thread.h"
32
#include "odp-util.h"
33
#include "unixctl.h"
34
#include "util.h"
35
36
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
37
static struct classifier cls;   /* Tunnel ports. */
38
39
struct ip_device {
40
    struct netdev *dev;
41
    struct eth_addr mac;
42
    struct in6_addr *addr;
43
    int n_addr;
44
    uint64_t change_seq;
45
    struct ovs_list node;
46
    char dev_name[IFNAMSIZ];
47
};
48
49
static struct ovs_list addr_list;
50
51
struct tnl_port {
52
    odp_port_t port;
53
    struct ovs_refcount ref_cnt;
54
    ovs_be16 tp_port;
55
    uint8_t nw_proto;
56
    char dev_name[IFNAMSIZ];
57
    struct ovs_list node;
58
};
59
60
static struct ovs_list port_list;
61
62
struct tnl_port_in {
63
    struct cls_rule cr;
64
    odp_port_t portno;
65
    struct ovs_refcount ref_cnt;
66
    char dev_name[IFNAMSIZ];
67
};
68
69
static struct tnl_port_in *
70
tnl_port_cast(const struct cls_rule *cr)
71
0
{
72
0
    BUILD_ASSERT_DECL(offsetof(struct tnl_port_in, cr) == 0);
73
74
0
    return cr ? CONTAINER_OF(cr, struct tnl_port_in, cr) : NULL;
75
0
}
76
77
static void
78
tnl_port_free(struct tnl_port_in *p)
79
0
{
80
0
    cls_rule_destroy(&p->cr);
81
0
    free(p);
82
0
}
83
84
static void
85
tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
86
                   struct in6_addr *addr, uint8_t nw_proto, ovs_be16 tp_port)
87
0
{
88
0
    memset(flow, 0, sizeof *flow);
89
90
0
    flow->dl_dst = mac;
91
0
    if (IN6_IS_ADDR_V4MAPPED(addr)) {
92
0
        flow->dl_type = htons(ETH_TYPE_IP);
93
0
        flow->nw_dst = in6_addr_get_mapped_ipv4(addr);
94
0
    } else {
95
0
        flow->dl_type = htons(ETH_TYPE_IPV6);
96
0
        flow->ipv6_dst = *addr;
97
0
    }
98
99
0
    flow->nw_proto = nw_proto;
100
0
    flow->tp_dst = tp_port;
101
0
}
102
103
static void
104
map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
105
           uint8_t nw_proto, ovs_be16 tp_port, const char dev_name[])
106
0
{
107
0
    const struct cls_rule *cr;
108
0
    struct tnl_port_in *p;
109
0
    struct match match;
110
111
0
    memset(&match, 0, sizeof match);
112
0
    tnl_port_init_flow(&match.flow, mac, addr, nw_proto, tp_port);
113
114
0
    do {
115
0
        cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, NULL, NULL);
116
0
        p = tnl_port_cast(cr);
117
        /* Try again if the rule was released before we get the reference. */
118
0
    } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
119
120
0
    if (!p) {
121
0
        p = xzalloc(sizeof *p);
122
0
        p->portno = port;
123
124
0
        match.wc.masks.dl_type = OVS_BE16_MAX;
125
0
        match.wc.masks.nw_proto = 0xff;
126
         /* XXX: No fragments support. */
127
0
        match.wc.masks.nw_frag = FLOW_NW_FRAG_MASK;
128
129
        /* 'tp_port' is zero for GRE and SRv6 tunnels. In this case it
130
         * doesn't make sense to match on UDP port numbers. */
131
0
        if (tp_port) {
132
0
            match.wc.masks.tp_dst = OVS_BE16_MAX;
133
0
        }
134
0
        if (IN6_IS_ADDR_V4MAPPED(addr)) {
135
0
            match.wc.masks.nw_dst = OVS_BE32_MAX;
136
0
        } else {
137
0
            match.wc.masks.ipv6_dst = in6addr_exact;
138
0
        }
139
0
        match.wc.masks.vlans[0].tci = OVS_BE16_MAX;
140
0
        memset(&match.wc.masks.dl_dst, 0xff, sizeof (struct eth_addr));
141
142
0
        cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
143
0
        ovs_refcount_init(&p->ref_cnt);
144
0
        ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
145
146
0
        classifier_insert(&cls, &p->cr, OVS_VERSION_MIN, NULL, 0);
147
0
    }
148
0
}
149
150
static void
151
map_insert_ipdev__(struct ip_device *ip_dev, char dev_name[],
152
                   odp_port_t port, uint8_t nw_proto, ovs_be16 tp_port)
153
0
{
154
0
    if (ip_dev->n_addr) {
155
0
        int i;
156
157
0
        for (i = 0; i < ip_dev->n_addr; i++) {
158
0
            map_insert(port, ip_dev->mac, &ip_dev->addr[i],
159
0
                       nw_proto, tp_port, dev_name);
160
0
        }
161
0
    }
162
0
}
163
164
static void
165
tnl_type_to_nw_proto(const char type[], uint8_t nw_protos[2])
166
0
{
167
0
    nw_protos[0] = nw_protos[1] = 0;
168
169
0
    if (!strcmp(type, "geneve") || !strcmp(type, "vxlan") ||
170
0
        !strcmp(type, "gtpu")) {
171
0
        nw_protos[0] = IPPROTO_UDP;
172
0
    } else if (!strcmp(type, "gre") || !strcmp(type, "erspan") ||
173
0
               !strcmp(type, "ip6erspan") || !strcmp(type, "ip6gre")) {
174
0
        nw_protos[0] = IPPROTO_GRE;
175
0
    } else if (!strcmp(type, "srv6")) {
176
0
        nw_protos[0] = IPPROTO_IPIP;
177
0
        nw_protos[1] = IPPROTO_IPV6;
178
0
    }
179
0
}
180
181
static void
182
tnl_port_map_insert__(odp_port_t port, ovs_be16 tp_port,
183
                      const char dev_name[], uint8_t nw_proto)
184
0
{
185
0
    struct tnl_port *p;
186
0
    struct ip_device *ip_dev;
187
188
0
    ovs_mutex_lock(&mutex);
189
0
    LIST_FOR_EACH(p, node, &port_list) {
190
0
        if (p->port == port && p->nw_proto == nw_proto) {
191
0
            ovs_refcount_ref(&p->ref_cnt);
192
0
            goto out;
193
0
        }
194
0
    }
195
196
0
    p = xzalloc(sizeof *p);
197
0
    p->port = port;
198
0
    p->tp_port = tp_port;
199
0
    p->nw_proto = nw_proto;
200
0
    ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
201
0
    ovs_refcount_init(&p->ref_cnt);
202
0
    ovs_list_insert(&port_list, &p->node);
203
204
0
    LIST_FOR_EACH(ip_dev, node, &addr_list) {
205
0
        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
206
0
    }
207
208
0
out:
209
0
    ovs_mutex_unlock(&mutex);
210
0
}
211
212
void
213
tnl_port_map_insert(odp_port_t port, ovs_be16 tp_port,
214
                    const char dev_name[], const char type[])
215
0
{
216
0
    uint8_t nw_protos[2];
217
0
    int i;
218
219
0
    tnl_type_to_nw_proto(type, nw_protos);
220
221
0
    for (i = 0; i < 2; i++) {
222
0
        if (nw_protos[i]) {
223
0
            tnl_port_map_insert__(port, tp_port, dev_name, nw_protos[i]);
224
0
        }
225
0
    }
226
0
}
227
228
static void
229
tnl_port_unref(const struct cls_rule *cr)
230
0
{
231
0
    struct tnl_port_in *p = tnl_port_cast(cr);
232
233
0
    if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
234
0
        classifier_remove_assert(&cls, cr);
235
0
        ovsrcu_postpone(tnl_port_free, p);
236
0
    }
237
0
}
238
239
static void
240
map_delete(struct eth_addr mac, struct in6_addr *addr,
241
           ovs_be16 tp_port, uint8_t nw_proto)
242
0
{
243
0
    const struct cls_rule *cr;
244
0
    struct flow flow;
245
246
0
    tnl_port_init_flow(&flow, mac, addr, nw_proto, tp_port);
247
248
0
    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL);
249
0
    tnl_port_unref(cr);
250
0
}
251
252
static void
253
ipdev_map_delete(struct ip_device *ip_dev, ovs_be16 tp_port, uint8_t nw_proto)
254
0
{
255
0
    if (ip_dev->n_addr) {
256
0
        int i;
257
258
0
        for (i = 0; i < ip_dev->n_addr; i++) {
259
0
            map_delete(ip_dev->mac, &ip_dev->addr[i], tp_port, nw_proto);
260
0
        }
261
0
    }
262
0
}
263
264
static void
265
tnl_port_map_delete__(odp_port_t port, uint8_t nw_proto)
266
0
{
267
0
    struct tnl_port *p;
268
0
    struct ip_device *ip_dev;
269
270
0
    ovs_mutex_lock(&mutex);
271
0
    LIST_FOR_EACH_SAFE (p, node, &port_list) {
272
0
        if (p->port == port && p->nw_proto == nw_proto &&
273
0
                    ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
274
0
            ovs_list_remove(&p->node);
275
0
            LIST_FOR_EACH(ip_dev, node, &addr_list) {
276
0
                ipdev_map_delete(ip_dev, p->tp_port, p->nw_proto);
277
0
            }
278
0
            free(p);
279
0
            break;
280
0
        }
281
0
    }
282
0
    ovs_mutex_unlock(&mutex);
283
0
}
284
285
void
286
tnl_port_map_delete(odp_port_t port, const char type[])
287
0
{
288
0
    uint8_t nw_protos[2];
289
0
    int i;
290
291
0
    tnl_type_to_nw_proto(type, nw_protos);
292
293
0
    for (i = 0; i < 2; i++) {
294
0
        if (nw_protos[i]) {
295
0
            tnl_port_map_delete__(port, nw_protos[i]);
296
0
        }
297
0
    }
298
0
}
299
300
/* 'flow' is non-const to allow for temporary modifications during the lookup.
301
 * Any changes are restored before returning. */
302
odp_port_t
303
tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
304
0
{
305
0
    const struct cls_rule *cr = classifier_lookup(&cls, OVS_VERSION_MAX, flow,
306
0
                                                  wc, NULL);
307
308
0
    return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
309
0
}
310
311
static void
312
tnl_port_show_v(struct ds *ds)
313
0
{
314
0
    const struct tnl_port_in *p;
315
316
0
    CLS_FOR_EACH(p, cr, &cls) {
317
0
        struct odputil_keybuf keybuf;
318
0
        struct odputil_keybuf maskbuf;
319
0
        struct flow flow;
320
0
        const struct nlattr *key, *mask;
321
0
        size_t key_len, mask_len;
322
0
        struct flow_wildcards wc;
323
0
        struct ofpbuf buf;
324
0
        struct odp_flow_key_parms odp_parms = {
325
0
            .flow = &flow,
326
0
            .mask = &wc.masks,
327
0
        };
328
329
0
        ds_put_format(ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
330
0
        minimask_expand(p->cr.match.mask, &wc);
331
0
        miniflow_expand(p->cr.match.flow, &flow);
332
333
        /* Key. */
334
0
        odp_parms.support.recirc = true;
335
0
        ofpbuf_use_stack(&buf, &keybuf, sizeof keybuf);
336
0
        odp_flow_key_from_flow(&odp_parms, &buf);
337
0
        key = buf.data;
338
0
        key_len = buf.size;
339
340
        /* mask*/
341
0
        odp_parms.support.recirc = false;
342
0
        ofpbuf_use_stack(&buf, &maskbuf, sizeof maskbuf);
343
0
        odp_flow_key_from_mask(&odp_parms, &buf);
344
0
        mask = buf.data;
345
0
        mask_len = buf.size;
346
347
        /* build string. */
348
0
        odp_flow_format(key, key_len, mask, mask_len, NULL, ds, false, false);
349
0
        ds_put_format(ds, "\n");
350
0
    }
351
0
}
352
353
static void
354
tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
355
               const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
356
0
{
357
0
    struct ds ds = DS_EMPTY_INITIALIZER;
358
0
    struct tnl_port *p;
359
360
0
    ds_put_format(&ds, "Listening ports:\n");
361
0
    ovs_mutex_lock(&mutex);
362
0
    if (argc > 1) {
363
0
        if (!strcasecmp(argv[1], "-v")) {
364
0
            tnl_port_show_v(&ds);
365
0
            goto out;
366
0
        }
367
0
    }
368
369
0
    LIST_FOR_EACH(p, node, &port_list) {
370
0
        ds_put_format(&ds, "%s (%"PRIu32") ref_cnt=%u\n", p->dev_name, p->port,
371
0
                      ovs_refcount_read(&p->ref_cnt));
372
0
    }
373
374
0
out:
375
0
    ovs_mutex_unlock(&mutex);
376
0
    unixctl_command_reply(conn, ds_cstr(&ds));
377
0
    ds_destroy(&ds);
378
0
}
379
380
static void
381
map_insert_ipdev(struct ip_device *ip_dev)
382
0
{
383
0
    struct tnl_port *p;
384
385
0
    LIST_FOR_EACH(p, node, &port_list) {
386
0
        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
387
0
    }
388
0
}
389
390
static void
391
insert_ipdev__(struct netdev *dev,
392
               struct in6_addr *addr, int n_addr)
393
0
{
394
0
    struct ip_device *ip_dev;
395
0
    enum netdev_flags flags;
396
0
    int error;
397
398
0
    error = netdev_get_flags(dev, &flags);
399
0
    if (error || (flags & NETDEV_LOOPBACK)) {
400
0
        goto err;
401
0
    }
402
403
0
    ip_dev = xzalloc(sizeof *ip_dev);
404
0
    ip_dev->dev = netdev_ref(dev);
405
0
    ip_dev->change_seq = netdev_get_change_seq(dev);
406
0
    error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
407
0
    if (error) {
408
0
        goto err_free_ipdev;
409
0
    }
410
0
    ip_dev->addr = addr;
411
0
    ip_dev->n_addr = n_addr;
412
0
    ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name);
413
0
    ovs_list_insert(&addr_list, &ip_dev->node);
414
0
    map_insert_ipdev(ip_dev);
415
0
    return;
416
417
0
err_free_ipdev:
418
0
    netdev_close(ip_dev->dev);
419
0
    free(ip_dev);
420
0
err:
421
0
    free(addr);
422
0
}
423
424
static void
425
insert_ipdev(const char dev_name[])
426
0
{
427
0
    struct in6_addr *addr, *mask;
428
0
    struct netdev *dev;
429
0
    int error, n_in6;
430
431
0
    error = netdev_open(dev_name, netdev_get_type_from_name(dev_name), &dev);
432
0
    if (error) {
433
0
        return;
434
0
    }
435
436
0
    error = netdev_get_addr_list(dev, &addr, &mask, &n_in6);
437
0
    if (error) {
438
0
        netdev_close(dev);
439
0
        return;
440
0
    }
441
0
    free(mask);
442
0
    insert_ipdev__(dev, addr, n_in6);
443
0
    netdev_close(dev);
444
0
}
445
446
static void
447
delete_ipdev(struct ip_device *ip_dev)
448
0
{
449
0
    struct tnl_port *p;
450
451
0
    LIST_FOR_EACH(p, node, &port_list) {
452
0
        ipdev_map_delete(ip_dev, p->tp_port, p->nw_proto);
453
0
    }
454
455
0
    ovs_list_remove(&ip_dev->node);
456
0
    netdev_close(ip_dev->dev);
457
0
    free(ip_dev->addr);
458
0
    free(ip_dev);
459
0
}
460
461
void
462
tnl_port_map_insert_ipdev(const char dev_name[])
463
0
{
464
0
    struct ip_device *ip_dev;
465
466
0
    ovs_mutex_lock(&mutex);
467
468
0
    LIST_FOR_EACH_SAFE (ip_dev, node, &addr_list) {
469
0
        if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
470
0
            if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
471
0
                goto out;
472
0
            }
473
            /* Address changed. */
474
0
            delete_ipdev(ip_dev);
475
0
        }
476
0
    }
477
0
    insert_ipdev(dev_name);
478
479
0
out:
480
0
    ovs_mutex_unlock(&mutex);
481
0
}
482
483
void
484
tnl_port_map_delete_ipdev(const char dev_name[])
485
0
{
486
0
    struct ip_device *ip_dev;
487
488
0
    ovs_mutex_lock(&mutex);
489
0
    LIST_FOR_EACH_SAFE (ip_dev, node, &addr_list) {
490
0
        if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
491
0
            delete_ipdev(ip_dev);
492
0
        }
493
0
    }
494
0
    ovs_mutex_unlock(&mutex);
495
0
}
496
497
void
498
tnl_port_map_run(void)
499
0
{
500
0
    struct ip_device *ip_dev;
501
502
0
    ovs_mutex_lock(&mutex);
503
0
    LIST_FOR_EACH_SAFE (ip_dev, node, &addr_list) {
504
0
        char dev_name[IFNAMSIZ];
505
506
0
        if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
507
0
            continue;
508
0
        }
509
510
        /* Address changed. */
511
0
        ovs_strlcpy_arrays(dev_name, ip_dev->dev_name);
512
0
        delete_ipdev(ip_dev);
513
0
        insert_ipdev(dev_name);
514
0
    }
515
0
    ovs_mutex_unlock(&mutex);
516
0
}
517
518
void
519
tnl_port_map_init(void)
520
0
{
521
0
    classifier_init(&cls, flow_segment_u64s);
522
0
    ovs_list_init(&addr_list);
523
0
    ovs_list_init(&port_list);
524
0
    unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
525
0
}