/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 | } |