Coverage Report

Created: 2023-03-26 07:41

/src/openvswitch/lib/tnl-neigh-cache.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014, 2015, 2016 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-neigh-cache.h"
20
21
#include <inttypes.h>
22
#include <sys/types.h>
23
#include <netinet/in.h>
24
#include <netinet/icmp6.h>
25
#include <stdlib.h>
26
27
#include "bitmap.h"
28
#include "cmap.h"
29
#include "coverage.h"
30
#include "dpif-netdev.h"
31
#include "openvswitch/dynamic-string.h"
32
#include "errno.h"
33
#include "flow.h"
34
#include "netdev.h"
35
#include "ovs-atomic.h"
36
#include "ovs-thread.h"
37
#include "packets.h"
38
#include "openvswitch/poll-loop.h"
39
#include "seq.h"
40
#include "socket-util.h"
41
#include "timeval.h"
42
#include "unaligned.h"
43
#include "unixctl.h"
44
#include "util.h"
45
#include "openvswitch/vlog.h"
46
47
48
#define NEIGH_ENTRY_DEFAULT_IDLE_TIME_MS  (15 * 60 * 1000)
49
0
#define NEIGH_ENTRY_MAX_AGING_TIME_S  3600
50
51
struct tnl_neigh_entry {
52
    struct cmap_node cmap_node;
53
    struct in6_addr ip;
54
    struct eth_addr mac;
55
    atomic_llong expires;       /* Expiration time in ms. */
56
    char br_name[IFNAMSIZ];
57
};
58
59
static struct cmap table = CMAP_INITIALIZER;
60
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
61
static atomic_uint32_t neigh_aging;
62
63
static uint32_t
64
tnl_neigh_hash(const struct in6_addr *ip)
65
0
{
66
0
    return hash_bytes(ip->s6_addr, 16, 0);
67
0
}
68
69
static bool
70
tnl_neigh_expired(struct tnl_neigh_entry *neigh)
71
0
{
72
0
    long long expires;
73
74
0
    atomic_read_explicit(&neigh->expires, &expires, memory_order_acquire);
75
76
0
    return expires <= time_msec();
77
0
}
78
79
static uint32_t
80
tnl_neigh_get_aging(void)
81
0
{
82
0
    unsigned int aging;
83
84
0
    atomic_read_explicit(&neigh_aging, &aging, memory_order_acquire);
85
0
    return aging;
86
0
}
87
88
static struct tnl_neigh_entry *
89
tnl_neigh_lookup__(const char br_name[IFNAMSIZ], const struct in6_addr *dst)
90
0
{
91
0
    struct tnl_neigh_entry *neigh;
92
0
    uint32_t hash;
93
94
0
    hash = tnl_neigh_hash(dst);
95
0
    CMAP_FOR_EACH_WITH_HASH (neigh, cmap_node, hash, &table) {
96
0
        if (ipv6_addr_equals(&neigh->ip, dst) && !strcmp(neigh->br_name, br_name)) {
97
0
            if (tnl_neigh_expired(neigh)) {
98
0
                return NULL;
99
0
            }
100
101
0
            atomic_store_explicit(&neigh->expires, time_msec() +
102
0
                                  tnl_neigh_get_aging(),
103
0
                                  memory_order_release);
104
0
            return neigh;
105
0
        }
106
0
    }
107
0
    return NULL;
108
0
}
109
110
int
111
tnl_neigh_lookup(const char br_name[IFNAMSIZ], const struct in6_addr *dst,
112
                 struct eth_addr *mac)
113
0
{
114
0
    struct tnl_neigh_entry *neigh;
115
0
    int res = ENOENT;
116
117
0
    neigh = tnl_neigh_lookup__(br_name, dst);
118
0
    if (neigh) {
119
0
        *mac = neigh->mac;
120
0
        res = 0;
121
0
    }
122
0
    return res;
123
0
}
124
125
static void
126
neigh_entry_free(struct tnl_neigh_entry *neigh)
127
0
{
128
0
    free(neigh);
129
0
}
130
131
static void
132
tnl_neigh_delete(struct tnl_neigh_entry *neigh)
133
0
{
134
0
    uint32_t hash = tnl_neigh_hash(&neigh->ip);
135
0
    cmap_remove(&table, &neigh->cmap_node, hash);
136
0
    ovsrcu_postpone(neigh_entry_free, neigh);
137
0
}
138
139
void
140
tnl_neigh_set(const char name[IFNAMSIZ], const struct in6_addr *dst,
141
              const struct eth_addr mac)
142
0
{
143
0
    ovs_mutex_lock(&mutex);
144
0
    struct tnl_neigh_entry *neigh = tnl_neigh_lookup__(name, dst);
145
0
    if (neigh) {
146
0
        if (eth_addr_equals(neigh->mac, mac)) {
147
0
            atomic_store_relaxed(&neigh->expires, time_msec() +
148
0
                                 tnl_neigh_get_aging());
149
0
            ovs_mutex_unlock(&mutex);
150
0
            return;
151
0
        }
152
0
        tnl_neigh_delete(neigh);
153
0
    }
154
0
    seq_change(tnl_conf_seq);
155
156
0
    neigh = xmalloc(sizeof *neigh);
157
158
0
    neigh->ip = *dst;
159
0
    neigh->mac = mac;
160
0
    atomic_store_relaxed(&neigh->expires, time_msec() +
161
0
                         tnl_neigh_get_aging());
162
0
    ovs_strlcpy(neigh->br_name, name, sizeof neigh->br_name);
163
0
    cmap_insert(&table, &neigh->cmap_node, tnl_neigh_hash(&neigh->ip));
164
0
    ovs_mutex_unlock(&mutex);
165
0
}
166
167
static void
168
tnl_arp_set(const char name[IFNAMSIZ], ovs_be32 dst,
169
            const struct eth_addr mac)
170
0
{
171
0
    struct in6_addr dst6 = in6_addr_mapped_ipv4(dst);
172
0
    tnl_neigh_set(name, &dst6, mac);
173
0
}
174
175
static int
176
tnl_arp_snoop(const struct flow *flow, struct flow_wildcards *wc,
177
              const char name[IFNAMSIZ], bool allow_update)
178
0
{
179
    /* Snoop normal ARP replies and gratuitous ARP requests/replies only */
180
0
    if (!is_arp(flow)
181
0
        || (!is_garp(flow, wc) &&
182
0
            FLOW_WC_GET_AND_MASK_WC(flow, wc, nw_proto) != ARP_OP_REPLY)
183
0
        || eth_addr_is_zero(FLOW_WC_GET_AND_MASK_WC(flow, wc, arp_sha))) {
184
0
        return EINVAL;
185
0
    }
186
187
0
    memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
188
189
0
    if (allow_update) {
190
0
        tnl_arp_set(name, flow->nw_src, flow->arp_sha);
191
0
    }
192
0
    return 0;
193
0
}
194
195
static int
196
tnl_nd_snoop(const struct flow *flow, struct flow_wildcards *wc,
197
             const char name[IFNAMSIZ], bool allow_update)
198
0
{
199
0
    if (!is_nd(flow, wc) || flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) {
200
0
        return EINVAL;
201
0
    }
202
    /* - RFC4861 says Neighbor Advertisements sent in response to unicast Neighbor
203
     *   Solicitations SHOULD include the Target link-layer address. However, Linux
204
     *   doesn't. So, the response to Solicitations sent by OVS will include the
205
     *   TLL address and other Advertisements not including it can be ignored.
206
     * - OVS flow extract can set this field to zero in case of packet parsing errors.
207
     *   For details refer miniflow_extract()*/
208
0
    if (eth_addr_is_zero(FLOW_WC_GET_AND_MASK_WC(flow, wc, arp_tha))) {
209
0
        return EINVAL;
210
0
    }
211
212
0
    memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
213
0
    memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
214
0
    memset(&wc->masks.nd_target, 0xff, sizeof wc->masks.nd_target);
215
216
0
    if (allow_update) {
217
0
        tnl_neigh_set(name, &flow->nd_target, flow->arp_tha);
218
0
    }
219
0
    return 0;
220
0
}
221
222
int
223
tnl_neigh_snoop(const struct flow *flow, struct flow_wildcards *wc,
224
                const char name[IFNAMSIZ], bool allow_update)
225
0
{
226
0
    int res;
227
0
    res = tnl_arp_snoop(flow, wc, name, allow_update);
228
0
    if (res != EINVAL) {
229
0
        return res;
230
0
    }
231
0
    return tnl_nd_snoop(flow, wc, name, allow_update);
232
0
}
233
234
void
235
tnl_neigh_cache_run(void)
236
0
{
237
0
    struct tnl_neigh_entry *neigh;
238
0
    bool changed = false;
239
240
0
    ovs_mutex_lock(&mutex);
241
0
    CMAP_FOR_EACH(neigh, cmap_node, &table) {
242
0
        if (tnl_neigh_expired(neigh)) {
243
0
            tnl_neigh_delete(neigh);
244
0
            changed = true;
245
0
        }
246
0
    }
247
0
    ovs_mutex_unlock(&mutex);
248
249
0
    if (changed) {
250
0
        seq_change(tnl_conf_seq);
251
0
    }
252
0
}
253
254
void
255
tnl_neigh_flush(const char br_name[IFNAMSIZ])
256
0
{
257
0
    struct tnl_neigh_entry *neigh;
258
0
    bool changed = false;
259
260
0
    ovs_mutex_lock(&mutex);
261
0
    CMAP_FOR_EACH (neigh, cmap_node, &table) {
262
0
        if (!strcmp(neigh->br_name, br_name)) {
263
0
            tnl_neigh_delete(neigh);
264
0
            changed = true;
265
0
        }
266
0
    }
267
0
    ovs_mutex_unlock(&mutex);
268
269
0
    if (changed) {
270
0
        seq_change(tnl_conf_seq);
271
0
    }
272
0
}
273
274
static void
275
tnl_neigh_cache_flush(struct unixctl_conn *conn, int argc OVS_UNUSED,
276
                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
277
0
{
278
0
    struct tnl_neigh_entry *neigh;
279
0
    bool changed = false;
280
281
0
    ovs_mutex_lock(&mutex);
282
0
    CMAP_FOR_EACH(neigh, cmap_node, &table) {
283
0
        tnl_neigh_delete(neigh);
284
0
        changed = true;
285
0
    }
286
0
    ovs_mutex_unlock(&mutex);
287
0
    if (changed) {
288
0
        seq_change(tnl_conf_seq);
289
0
    }
290
0
    unixctl_command_reply(conn, "OK");
291
0
}
292
293
static void
294
tnl_neigh_cache_aging(struct unixctl_conn *conn, int argc,
295
                        const char *argv[], void *aux OVS_UNUSED)
296
0
{
297
0
    long long int new_exp, curr_exp;
298
0
    struct tnl_neigh_entry *neigh;
299
0
    uint32_t aging;
300
301
0
    if (argc == 1) {
302
0
        struct ds ds = DS_EMPTY_INITIALIZER;
303
0
        ds_put_format(&ds, "%"PRIu32, tnl_neigh_get_aging() / 1000);
304
0
        unixctl_command_reply(conn, ds_cstr(&ds));
305
0
        ds_destroy(&ds);
306
307
0
        return;
308
0
    }
309
310
0
    if (!ovs_scan(argv[1], "%"SCNu32, &aging) ||
311
0
        !aging || aging > NEIGH_ENTRY_MAX_AGING_TIME_S) {
312
0
        unixctl_command_reply_error(conn, "bad aging value");
313
0
        return;
314
0
    }
315
316
0
    aging *= 1000;
317
0
    atomic_store_explicit(&neigh_aging, aging, memory_order_release);
318
0
    new_exp = time_msec() + aging;
319
320
0
    CMAP_FOR_EACH (neigh, cmap_node, &table) {
321
0
        atomic_read_explicit(&neigh->expires, &curr_exp,
322
0
                             memory_order_acquire);
323
0
        if (new_exp < curr_exp) {
324
0
            atomic_store_explicit(&neigh->expires, new_exp,
325
0
                                  memory_order_release);
326
0
        }
327
0
    }
328
329
0
    unixctl_command_reply(conn, "OK");
330
0
}
331
332
static int
333
lookup_any(const char *host_name, struct in6_addr *address)
334
0
{
335
0
    if (addr_is_ipv6(host_name)) {
336
0
        return lookup_ipv6(host_name, address);
337
0
    } else {
338
0
        int r;
339
0
        struct in_addr ip;
340
0
        r = lookup_ip(host_name, &ip);
341
0
        if (r == 0) {
342
0
            in6_addr_set_mapped_ipv4(address, ip.s_addr);
343
0
        }
344
0
        return r;
345
0
    }
346
0
    return ENOENT;
347
0
}
348
349
static void
350
tnl_neigh_cache_add(struct unixctl_conn *conn, int argc OVS_UNUSED,
351
                    const char *argv[], void *aux OVS_UNUSED)
352
0
{
353
0
    const char *br_name = argv[1];
354
0
    struct eth_addr mac;
355
0
    struct in6_addr ip6;
356
357
0
    if (lookup_any(argv[2], &ip6) != 0) {
358
0
        unixctl_command_reply_error(conn, "bad IP address");
359
0
        return;
360
0
    }
361
362
0
    if (!eth_addr_from_string(argv[3], &mac)) {
363
0
        unixctl_command_reply_error(conn, "bad MAC address");
364
0
        return;
365
0
    }
366
367
0
    tnl_neigh_set(br_name, &ip6, mac);
368
0
    unixctl_command_reply(conn, "OK");
369
0
}
370
371
static void
372
tnl_neigh_cache_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
373
                     const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
374
0
{
375
0
    struct ds ds = DS_EMPTY_INITIALIZER;
376
0
    struct tnl_neigh_entry *neigh;
377
378
0
    ds_put_cstr(&ds, "IP                                            MAC                 Bridge\n");
379
0
    ds_put_cstr(&ds, "==========================================================================\n");
380
0
    ovs_mutex_lock(&mutex);
381
0
    CMAP_FOR_EACH(neigh, cmap_node, &table) {
382
0
        int start_len, need_ws;
383
384
0
        start_len = ds.length;
385
0
        ipv6_format_mapped(&neigh->ip, &ds);
386
387
0
        need_ws = INET6_ADDRSTRLEN - (ds.length - start_len);
388
0
        ds_put_char_multiple(&ds, ' ', need_ws);
389
390
0
        ds_put_format(&ds, ETH_ADDR_FMT"   %s",
391
0
                      ETH_ADDR_ARGS(neigh->mac), neigh->br_name);
392
0
        if (tnl_neigh_expired(neigh)) {
393
0
            ds_put_format(&ds, " STALE");
394
0
        }
395
0
        ds_put_char(&ds, '\n');
396
397
0
    }
398
0
    ovs_mutex_unlock(&mutex);
399
0
    unixctl_command_reply(conn, ds_cstr(&ds));
400
0
    ds_destroy(&ds);
401
0
}
402
403
void
404
tnl_neigh_cache_init(void)
405
0
{
406
0
    atomic_init(&neigh_aging, NEIGH_ENTRY_DEFAULT_IDLE_TIME_MS);
407
0
    unixctl_command_register("tnl/arp/show", "", 0, 0,
408
0
                             tnl_neigh_cache_show, NULL);
409
0
    unixctl_command_register("tnl/arp/set", "BRIDGE IP MAC", 3, 3,
410
0
                             tnl_neigh_cache_add, NULL);
411
0
    unixctl_command_register("tnl/arp/flush", "", 0, 0,
412
0
                             tnl_neigh_cache_flush, NULL);
413
0
    unixctl_command_register("tnl/arp/aging", "[SECS]", 0, 1,
414
0
                             tnl_neigh_cache_aging, NULL);
415
0
    unixctl_command_register("tnl/neigh/show", "", 0, 0,
416
0
                             tnl_neigh_cache_show, NULL);
417
0
    unixctl_command_register("tnl/neigh/set", "BRIDGE IP MAC", 3, 3,
418
0
                             tnl_neigh_cache_add, NULL);
419
0
    unixctl_command_register("tnl/neigh/flush", "", 0, 0,
420
0
                             tnl_neigh_cache_flush, NULL);
421
0
    unixctl_command_register("tnl/neigh/aging", "[SECS]", 0, 1,
422
0
                             tnl_neigh_cache_aging, NULL);
423
0
}