]>
Commit | Line | Data |
---|---|---|
50f96b10 | 1 | /* Copyright (c) 2013, 2014, 2015, 2017 Nicira, Inc. |
7d1a8e7a JG |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. */ | |
14 | ||
15 | #include <config.h> | |
16 | #include "tunnel.h" | |
17 | ||
18 | #include <errno.h> | |
19 | ||
7d1a8e7a | 20 | #include "byte-order.h" |
da4a6191 | 21 | #include "connectivity.h" |
a36de779 PS |
22 | #include "csum.h" |
23 | #include "dpif.h" | |
3e8a2ad1 | 24 | #include "openvswitch/dynamic-string.h" |
493823d8 | 25 | #include "fat-rwlock.h" |
7d1a8e7a | 26 | #include "hash.h" |
ee89ea7b | 27 | #include "openvswitch/hmap.h" |
e1939dbf | 28 | #include "netdev.h" |
7d1a8e7a | 29 | #include "odp-util.h" |
64c96779 | 30 | #include "openvswitch/ofpbuf.h" |
7d1a8e7a | 31 | #include "packets.h" |
a36de779 | 32 | #include "route-table.h" |
da4a6191 | 33 | #include "seq.h" |
7d1a8e7a JG |
34 | #include "smap.h" |
35 | #include "socket-util.h" | |
a36de779 | 36 | #include "tnl-ports.h" |
7d1a8e7a | 37 | #include "tunnel.h" |
e6211adc | 38 | #include "openvswitch/vlog.h" |
a36de779 PS |
39 | #include "unaligned.h" |
40 | #include "ofproto-dpif.h" | |
7d1a8e7a | 41 | |
7d1a8e7a JG |
42 | VLOG_DEFINE_THIS_MODULE(tunnel); |
43 | ||
44 | struct tnl_match { | |
45 | ovs_be64 in_key; | |
b863fb65 JB |
46 | struct in6_addr ipv6_src; |
47 | struct in6_addr ipv6_dst; | |
4e022ec0 | 48 | odp_port_t odp_port; |
7d1a8e7a | 49 | bool in_key_flow; |
0ad90c84 JR |
50 | bool ip_src_flow; |
51 | bool ip_dst_flow; | |
7d1a8e7a JG |
52 | }; |
53 | ||
54 | struct tnl_port { | |
42943cde | 55 | struct hmap_node ofport_node; |
7d1a8e7a JG |
56 | struct hmap_node match_node; |
57 | ||
e1939dbf | 58 | const struct ofport_dpif *ofport; |
7631e9bc | 59 | uint64_t change_seq; |
e1939dbf EJ |
60 | struct netdev *netdev; |
61 | ||
7d1a8e7a JG |
62 | struct tnl_match match; |
63 | }; | |
64 | ||
493823d8 | 65 | static struct fat_rwlock rwlock; |
fa117360 | 66 | |
c3fd6901 BP |
67 | /* Tunnel matches. |
68 | * | |
69 | * This module maps packets received over tunnel protocols to vports. The | |
70 | * tunnel protocol and, for some protocols, tunnel-specific information (e.g., | |
71 | * for VXLAN, the UDP destination port number) are always use as part of the | |
72 | * mapping. Which other fields are used for the mapping depends on the vports | |
73 | * themselves (the parenthesized notations refer to "struct tnl_match" fields): | |
74 | * | |
75 | * - in_key: A vport may match a specific tunnel ID (in_key_flow == false) | |
76 | * or arrange for the tunnel ID to be matched as tunnel.tun_id in the | |
77 | * OpenFlow flow (in_key_flow == true). | |
78 | * | |
79 | * - ip_dst: A vport may match a specific destination IP address | |
80 | * (ip_dst_flow == false) or arrange for the destination IP to be matched | |
81 | * as tunnel.ip_dst in the OpenFlow flow (ip_dst_flow == true). | |
82 | * | |
83 | * - ip_src: A vport may match a specific IP source address (ip_src_flow == | |
84 | * false, ip_src != 0), wildcard all source addresses (ip_src_flow == | |
85 | * false, ip_src == 0), or arrange for the IP source address to be | |
86 | * handled in the OpenFlow flow table (ip_src_flow == true). | |
87 | * | |
88 | * Thus, there are 2 * 2 * 3 == 12 possible ways a vport can match against a | |
89 | * tunnel packet. We number the possibilities for each field in increasing | |
90 | * order as listed in each bullet above. We order the 12 overall combinations | |
91 | * in lexicographic order considering in_key first, then ip_dst, then | |
92 | * ip_src. */ | |
93 | #define N_MATCH_TYPES (2 * 2 * 3) | |
94 | ||
95 | /* The three possibilities (see above) for vport ip_src matches. */ | |
96 | enum ip_src_type { | |
97 | IP_SRC_CFG, /* ip_src must equal configured address. */ | |
98 | IP_SRC_ANY, /* Any ip_src is acceptable. */ | |
99 | IP_SRC_FLOW /* ip_src is handled in flow table. */ | |
100 | }; | |
101 | ||
102 | /* Each hmap contains "struct tnl_port"s. | |
103 | * The index is a combination of how each of the fields listed under "Tunnel | |
104 | * matches" above matches, see the final paragraph for ordering. */ | |
105 | static struct hmap *tnl_match_maps[N_MATCH_TYPES] OVS_GUARDED_BY(rwlock); | |
106 | static struct hmap **tnl_match_map(const struct tnl_match *); | |
fa117360 EJ |
107 | |
108 | static struct hmap ofport_map__ = HMAP_INITIALIZER(&ofport_map__); | |
109 | static struct hmap *ofport_map OVS_GUARDED_BY(rwlock) = &ofport_map__; | |
7d1a8e7a JG |
110 | |
111 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
112 | static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60); | |
113 | ||
32d0eee8 | 114 | static struct tnl_port *tnl_find(const struct flow *) OVS_REQ_RDLOCK(rwlock); |
c3fd6901 | 115 | static struct tnl_port *tnl_find_exact(struct tnl_match *, struct hmap *) |
344e21d4 | 116 | OVS_REQ_RDLOCK(rwlock); |
fa117360 | 117 | static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *) |
344e21d4 | 118 | OVS_REQ_RDLOCK(rwlock); |
42943cde | 119 | |
7d1a8e7a JG |
120 | static uint32_t tnl_hash(struct tnl_match *); |
121 | static void tnl_match_fmt(const struct tnl_match *, struct ds *); | |
344e21d4 | 122 | static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock); |
fa117360 | 123 | static void tnl_port_mod_log(const struct tnl_port *, const char *action) |
344e21d4 | 124 | OVS_REQ_RDLOCK(rwlock); |
fa117360 | 125 | static const char *tnl_port_get_name(const struct tnl_port *) |
344e21d4 AW |
126 | OVS_REQ_RDLOCK(rwlock); |
127 | static void tnl_port_del__(const struct ofport_dpif *) OVS_REQ_WRLOCK(rwlock); | |
7d1a8e7a | 128 | |
493823d8 PS |
129 | void |
130 | ofproto_tunnel_init(void) | |
131 | { | |
132 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; | |
133 | ||
134 | if (ovsthread_once_start(&once)) { | |
135 | fat_rwlock_init(&rwlock); | |
136 | ovsthread_once_done(&once); | |
137 | } | |
138 | } | |
139 | ||
42943cde | 140 | static bool |
e1939dbf | 141 | tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev, |
a36de779 | 142 | odp_port_t odp_port, bool warn, bool native_tnl, const char name[]) |
344e21d4 | 143 | OVS_REQ_WRLOCK(rwlock) |
7d1a8e7a JG |
144 | { |
145 | const struct netdev_tunnel_config *cfg; | |
146 | struct tnl_port *existing_port; | |
147 | struct tnl_port *tnl_port; | |
c3fd6901 | 148 | struct hmap **map; |
7d1a8e7a | 149 | |
e1939dbf | 150 | cfg = netdev_get_tunnel_config(netdev); |
7d1a8e7a JG |
151 | ovs_assert(cfg); |
152 | ||
153 | tnl_port = xzalloc(sizeof *tnl_port); | |
154 | tnl_port->ofport = ofport; | |
e1939dbf | 155 | tnl_port->netdev = netdev_ref(netdev); |
7631e9bc | 156 | tnl_port->change_seq = netdev_get_change_seq(tnl_port->netdev); |
7d1a8e7a JG |
157 | |
158 | tnl_port->match.in_key = cfg->in_key; | |
3ae91c01 JB |
159 | tnl_port->match.ipv6_src = cfg->ipv6_src; |
160 | tnl_port->match.ipv6_dst = cfg->ipv6_dst; | |
0ad90c84 JR |
161 | tnl_port->match.ip_src_flow = cfg->ip_src_flow; |
162 | tnl_port->match.ip_dst_flow = cfg->ip_dst_flow; | |
7d1a8e7a JG |
163 | tnl_port->match.in_key_flow = cfg->in_key_flow; |
164 | tnl_port->match.odp_port = odp_port; | |
165 | ||
c3fd6901 BP |
166 | map = tnl_match_map(&tnl_port->match); |
167 | existing_port = tnl_find_exact(&tnl_port->match, *map); | |
7d1a8e7a JG |
168 | if (existing_port) { |
169 | if (warn) { | |
170 | struct ds ds = DS_EMPTY_INITIALIZER; | |
171 | tnl_match_fmt(&tnl_port->match, &ds); | |
172 | VLOG_WARN("%s: attempting to add tunnel port with same config as " | |
173 | "port '%s' (%s)", tnl_port_get_name(tnl_port), | |
174 | tnl_port_get_name(existing_port), ds_cstr(&ds)); | |
175 | ds_destroy(&ds); | |
7d1a8e7a | 176 | } |
780f7a2e JS |
177 | netdev_close(tnl_port->netdev); |
178 | free(tnl_port); | |
42943cde | 179 | return false; |
7d1a8e7a JG |
180 | } |
181 | ||
fa117360 | 182 | hmap_insert(ofport_map, &tnl_port->ofport_node, hash_pointer(ofport, 0)); |
c3fd6901 BP |
183 | |
184 | if (!*map) { | |
185 | *map = xmalloc(sizeof **map); | |
186 | hmap_init(*map); | |
187 | } | |
188 | hmap_insert(*map, &tnl_port->match_node, tnl_hash(&tnl_port->match)); | |
7d1a8e7a | 189 | tnl_port_mod_log(tnl_port, "adding"); |
a36de779 PS |
190 | |
191 | if (native_tnl) { | |
98e3f58e PS |
192 | const char *type; |
193 | ||
194 | type = netdev_get_type(netdev); | |
195 | tnl_port_map_insert(odp_port, cfg->dst_port, name, type); | |
196 | ||
a36de779 | 197 | } |
42943cde | 198 | return true; |
7d1a8e7a JG |
199 | } |
200 | ||
201 | /* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's | |
202 | * must be added before they can be used by the module. 'ofport' must be a | |
ea0797c9 BP |
203 | * tunnel. |
204 | * | |
205 | * Returns 0 if successful, otherwise a positive errno value. */ | |
206 | int | |
e1939dbf | 207 | tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev, |
a36de779 | 208 | odp_port_t odp_port, bool native_tnl, const char name[]) OVS_EXCLUDED(rwlock) |
7d1a8e7a | 209 | { |
ea0797c9 BP |
210 | bool ok; |
211 | ||
493823d8 | 212 | fat_rwlock_wrlock(&rwlock); |
ea0797c9 | 213 | ok = tnl_port_add__(ofport, netdev, odp_port, true, native_tnl, name); |
493823d8 | 214 | fat_rwlock_unlock(&rwlock); |
ea0797c9 BP |
215 | |
216 | return ok ? 0 : EEXIST; | |
7d1a8e7a JG |
217 | } |
218 | ||
42943cde EJ |
219 | /* Checks if the tunnel represented by 'ofport' reconfiguration due to changes |
220 | * in its netdev_tunnel_config. If it does, returns true. Otherwise, returns | |
221 | * false. 'ofport' and 'odp_port' should be the same as would be passed to | |
7d1a8e7a JG |
222 | * tnl_port_add(). */ |
223 | bool | |
e1939dbf | 224 | tnl_port_reconfigure(const struct ofport_dpif *ofport, |
a36de779 PS |
225 | const struct netdev *netdev, odp_port_t odp_port, |
226 | bool native_tnl, const char name[]) | |
fa117360 | 227 | OVS_EXCLUDED(rwlock) |
7d1a8e7a | 228 | { |
fa117360 EJ |
229 | struct tnl_port *tnl_port; |
230 | bool changed = false; | |
7d1a8e7a | 231 | |
493823d8 | 232 | fat_rwlock_wrlock(&rwlock); |
fa117360 | 233 | tnl_port = tnl_find_ofport(ofport); |
42943cde | 234 | if (!tnl_port) { |
a36de779 | 235 | changed = tnl_port_add__(ofport, netdev, odp_port, false, native_tnl, name); |
42943cde | 236 | } else if (tnl_port->netdev != netdev |
7d1a8e7a | 237 | || tnl_port->match.odp_port != odp_port |
7631e9bc | 238 | || tnl_port->change_seq != netdev_get_change_seq(tnl_port->netdev)) { |
7d1a8e7a | 239 | VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port)); |
fa117360 | 240 | tnl_port_del__(ofport); |
a36de779 | 241 | tnl_port_add__(ofport, netdev, odp_port, true, native_tnl, name); |
fa117360 | 242 | changed = true; |
7d1a8e7a | 243 | } |
493823d8 | 244 | fat_rwlock_unlock(&rwlock); |
fa117360 | 245 | return changed; |
7d1a8e7a JG |
246 | } |
247 | ||
fa117360 EJ |
248 | static void |
249 | tnl_port_del__(const struct ofport_dpif *ofport) OVS_REQ_WRLOCK(rwlock) | |
7d1a8e7a | 250 | { |
fa117360 EJ |
251 | struct tnl_port *tnl_port; |
252 | ||
253 | if (!ofport) { | |
254 | return; | |
255 | } | |
42943cde | 256 | |
fa117360 | 257 | tnl_port = tnl_find_ofport(ofport); |
42943cde | 258 | if (tnl_port) { |
a36de779 PS |
259 | const struct netdev_tunnel_config *cfg = |
260 | netdev_get_tunnel_config(tnl_port->netdev); | |
c3fd6901 BP |
261 | struct hmap **map; |
262 | ||
98e3f58e | 263 | tnl_port_map_delete(cfg->dst_port, netdev_get_type(tnl_port->netdev)); |
7d1a8e7a | 264 | tnl_port_mod_log(tnl_port, "removing"); |
c3fd6901 BP |
265 | map = tnl_match_map(&tnl_port->match); |
266 | hmap_remove(*map, &tnl_port->match_node); | |
267 | if (hmap_is_empty(*map)) { | |
268 | hmap_destroy(*map); | |
269 | free(*map); | |
270 | *map = NULL; | |
271 | } | |
fa117360 | 272 | hmap_remove(ofport_map, &tnl_port->ofport_node); |
e1939dbf | 273 | netdev_close(tnl_port->netdev); |
7d1a8e7a JG |
274 | free(tnl_port); |
275 | } | |
276 | } | |
277 | ||
fa117360 EJ |
278 | /* Removes 'ofport' from the module. */ |
279 | void | |
280 | tnl_port_del(const struct ofport_dpif *ofport) OVS_EXCLUDED(rwlock) | |
281 | { | |
493823d8 | 282 | fat_rwlock_wrlock(&rwlock); |
fa117360 | 283 | tnl_port_del__(ofport); |
493823d8 | 284 | fat_rwlock_unlock(&rwlock); |
fa117360 EJ |
285 | } |
286 | ||
2301f7eb BP |
287 | /* Looks in the table of tunnels for a tunnel matching the metadata in 'flow'. |
288 | * Returns the 'ofport' corresponding to the new in_port, or a null pointer if | |
289 | * none is found. | |
7d1a8e7a JG |
290 | * |
291 | * Callers should verify that 'flow' needs to be received by calling | |
2301f7eb | 292 | * tnl_port_should_receive() before this function. */ |
e1939dbf | 293 | const struct ofport_dpif * |
fa117360 | 294 | tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock) |
7d1a8e7a JG |
295 | { |
296 | char *pre_flow_str = NULL; | |
fa117360 | 297 | const struct ofport_dpif *ofport; |
7d1a8e7a | 298 | struct tnl_port *tnl_port; |
7d1a8e7a | 299 | |
493823d8 | 300 | fat_rwlock_rdlock(&rwlock); |
32d0eee8 | 301 | tnl_port = tnl_find(flow); |
fa117360 | 302 | ofport = tnl_port ? tnl_port->ofport : NULL; |
7d1a8e7a | 303 | if (!tnl_port) { |
50f96b10 | 304 | char *flow_str = flow_to_string(flow, NULL); |
7d1a8e7a | 305 | |
32d0eee8 BP |
306 | VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", flow_str); |
307 | free(flow_str); | |
fa117360 | 308 | goto out; |
7d1a8e7a JG |
309 | } |
310 | ||
7d1a8e7a | 311 | if (!VLOG_DROP_DBG(&dbg_rl)) { |
50f96b10 | 312 | pre_flow_str = flow_to_string(flow, NULL); |
7d1a8e7a JG |
313 | } |
314 | ||
7d1a8e7a | 315 | if (pre_flow_str) { |
50f96b10 | 316 | char *post_flow_str = flow_to_string(flow, NULL); |
7d1a8e7a JG |
317 | char *tnl_str = tnl_port_fmt(tnl_port); |
318 | VLOG_DBG("flow received\n" | |
319 | "%s" | |
320 | " pre: %s\n" | |
321 | "post: %s", | |
322 | tnl_str, pre_flow_str, post_flow_str); | |
323 | free(tnl_str); | |
324 | free(pre_flow_str); | |
325 | free(post_flow_str); | |
326 | } | |
fa117360 EJ |
327 | |
328 | out: | |
493823d8 | 329 | fat_rwlock_unlock(&rwlock); |
fa117360 | 330 | return ofport; |
7d1a8e7a JG |
331 | } |
332 | ||
62a7cc71 JG |
333 | /* Should be called at the beginning of action translation to initialize |
334 | * wildcards and perform any actions based on receiving on tunnel port. | |
335 | * | |
336 | * Returns false if the packet must be dropped. */ | |
337 | bool | |
227a0141 | 338 | tnl_process_ecn(struct flow *flow) |
62a7cc71 | 339 | { |
227a0141 BP |
340 | if (!tnl_port_should_receive(flow)) { |
341 | return true; | |
342 | } | |
343 | ||
1bc3f0ed | 344 | if (is_ip_any(flow) && IP_ECN_is_ce(flow->tunnel.ip_tos)) { |
227a0141 BP |
345 | if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) { |
346 | VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE" | |
347 | " but is not ECN capable"); | |
62a7cc71 JG |
348 | return false; |
349 | } | |
ed2e7245 | 350 | |
227a0141 BP |
351 | /* Set the ECN CE value in the tunneled packet. */ |
352 | flow->nw_tos |= IP_ECN_CE; | |
62a7cc71 JG |
353 | } |
354 | ||
355 | return true; | |
356 | } | |
357 | ||
227a0141 BP |
358 | void |
359 | tnl_wc_init(struct flow *flow, struct flow_wildcards *wc) | |
360 | { | |
361 | if (tnl_port_should_receive(flow)) { | |
362 | wc->masks.tunnel.tun_id = OVS_BE64_MAX; | |
ffe4c74f JB |
363 | if (flow->tunnel.ip_dst) { |
364 | wc->masks.tunnel.ip_src = OVS_BE32_MAX; | |
365 | wc->masks.tunnel.ip_dst = OVS_BE32_MAX; | |
366 | } else { | |
367 | wc->masks.tunnel.ipv6_src = in6addr_exact; | |
368 | wc->masks.tunnel.ipv6_dst = in6addr_exact; | |
369 | } | |
227a0141 BP |
370 | wc->masks.tunnel.flags = (FLOW_TNL_F_DONT_FRAGMENT | |
371 | FLOW_TNL_F_CSUM | | |
372 | FLOW_TNL_F_KEY); | |
373 | wc->masks.tunnel.ip_tos = UINT8_MAX; | |
fba633f1 | 374 | wc->masks.tunnel.ip_ttl = 0; |
227a0141 BP |
375 | /* The tp_src and tp_dst members in flow_tnl are set to be always |
376 | * wildcarded, not to unwildcard them here. */ | |
377 | wc->masks.tunnel.tp_src = 0; | |
378 | wc->masks.tunnel.tp_dst = 0; | |
379 | ||
227a0141 | 380 | if (is_ip_any(flow) |
1bc3f0ed | 381 | && IP_ECN_is_ce(flow->tunnel.ip_tos)) { |
227a0141 BP |
382 | wc->masks.nw_tos |= IP_ECN_MASK; |
383 | } | |
beb75a40 JS |
384 | /* Match on packet_type for tunneled packets.*/ |
385 | wc->masks.packet_type = OVS_BE32_MAX; | |
386 | ||
227a0141 BP |
387 | } |
388 | } | |
389 | ||
7d1a8e7a JG |
390 | /* Given that 'flow' should be output to the ofport corresponding to |
391 | * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath | |
4e022ec0 | 392 | * port that the output should happen on. May return ODPP_NONE if the output |
7d1a8e7a | 393 | * shouldn't occur. */ |
4e022ec0 | 394 | odp_port_t |
42943cde | 395 | tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow, |
fa117360 | 396 | struct flow_wildcards *wc) OVS_EXCLUDED(rwlock) |
7d1a8e7a JG |
397 | { |
398 | const struct netdev_tunnel_config *cfg; | |
fa117360 | 399 | struct tnl_port *tnl_port; |
7d1a8e7a | 400 | char *pre_flow_str = NULL; |
fa117360 | 401 | odp_port_t out_port; |
7d1a8e7a | 402 | |
493823d8 | 403 | fat_rwlock_rdlock(&rwlock); |
fa117360 EJ |
404 | tnl_port = tnl_find_ofport(ofport); |
405 | out_port = tnl_port ? tnl_port->match.odp_port : ODPP_NONE; | |
42943cde | 406 | if (!tnl_port) { |
fa117360 | 407 | goto out; |
7d1a8e7a JG |
408 | } |
409 | ||
e1939dbf | 410 | cfg = netdev_get_tunnel_config(tnl_port->netdev); |
7d1a8e7a JG |
411 | ovs_assert(cfg); |
412 | ||
413 | if (!VLOG_DROP_DBG(&dbg_rl)) { | |
50f96b10 | 414 | pre_flow_str = flow_to_string(flow, NULL); |
7d1a8e7a JG |
415 | } |
416 | ||
0ad90c84 | 417 | if (!cfg->ip_src_flow) { |
b863fb65 | 418 | flow->tunnel.ip_src = in6_addr_get_mapped_ipv4(&tnl_port->match.ipv6_src); |
ffe4c74f JB |
419 | if (!flow->tunnel.ip_src) { |
420 | flow->tunnel.ipv6_src = tnl_port->match.ipv6_src; | |
110f7c75 TLSC |
421 | } else { |
422 | flow->tunnel.ipv6_src = in6addr_any; | |
ffe4c74f | 423 | } |
0ad90c84 JR |
424 | } |
425 | if (!cfg->ip_dst_flow) { | |
b863fb65 | 426 | flow->tunnel.ip_dst = in6_addr_get_mapped_ipv4(&tnl_port->match.ipv6_dst); |
ffe4c74f JB |
427 | if (!flow->tunnel.ip_dst) { |
428 | flow->tunnel.ipv6_dst = tnl_port->match.ipv6_dst; | |
110f7c75 TLSC |
429 | } else { |
430 | flow->tunnel.ipv6_dst = in6addr_any; | |
ffe4c74f | 431 | } |
0ad90c84 | 432 | } |
998300ec | 433 | flow->tunnel.tp_dst = cfg->dst_port; |
7d1a8e7a JG |
434 | if (!cfg->out_key_flow) { |
435 | flow->tunnel.tun_id = cfg->out_key; | |
436 | } | |
437 | ||
438 | if (cfg->ttl_inherit && is_ip_any(flow)) { | |
d4f4a9b2 | 439 | wc->masks.nw_ttl = 0xff; |
7d1a8e7a JG |
440 | flow->tunnel.ip_ttl = flow->nw_ttl; |
441 | } else { | |
442 | flow->tunnel.ip_ttl = cfg->ttl; | |
443 | } | |
444 | ||
445 | if (cfg->tos_inherit && is_ip_any(flow)) { | |
cb85b49c | 446 | wc->masks.nw_tos |= IP_DSCP_MASK; |
7d1a8e7a JG |
447 | flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK; |
448 | } else { | |
449 | flow->tunnel.ip_tos = cfg->tos; | |
450 | } | |
451 | ||
abcd4402 JP |
452 | /* ECN fields are always inherited. */ |
453 | if (is_ip_any(flow)) { | |
454 | wc->masks.nw_tos |= IP_ECN_MASK; | |
abcd4402 | 455 | |
1bc3f0ed | 456 | if (IP_ECN_is_ce(flow->nw_tos)) { |
1ac9326d JR |
457 | flow->tunnel.ip_tos |= IP_ECN_ECT_0; |
458 | } else { | |
459 | flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK; | |
460 | } | |
7d1a8e7a JG |
461 | } |
462 | ||
b666962b | 463 | flow->tunnel.flags |= (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0) |
7d1a8e7a JG |
464 | | (cfg->csum ? FLOW_TNL_F_CSUM : 0) |
465 | | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0); | |
466 | ||
bf4bbd0d PS |
467 | if (cfg->set_egress_pkt_mark) { |
468 | flow->pkt_mark = cfg->egress_pkt_mark; | |
469 | wc->masks.pkt_mark = UINT32_MAX; | |
470 | } | |
471 | ||
7d1a8e7a | 472 | if (pre_flow_str) { |
50f96b10 | 473 | char *post_flow_str = flow_to_string(flow, NULL); |
7d1a8e7a JG |
474 | char *tnl_str = tnl_port_fmt(tnl_port); |
475 | VLOG_DBG("flow sent\n" | |
476 | "%s" | |
477 | " pre: %s\n" | |
478 | "post: %s", | |
479 | tnl_str, pre_flow_str, post_flow_str); | |
480 | free(tnl_str); | |
481 | free(pre_flow_str); | |
482 | free(post_flow_str); | |
483 | } | |
484 | ||
fa117360 | 485 | out: |
493823d8 | 486 | fat_rwlock_unlock(&rwlock); |
fa117360 | 487 | return out_port; |
7d1a8e7a JG |
488 | } |
489 | ||
490 | static uint32_t | |
491 | tnl_hash(struct tnl_match *match) | |
492 | { | |
493 | BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0); | |
494 | return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0); | |
495 | } | |
496 | ||
42943cde | 497 | static struct tnl_port * |
fa117360 | 498 | tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock) |
42943cde EJ |
499 | { |
500 | struct tnl_port *tnl_port; | |
501 | ||
502 | HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0), | |
fa117360 | 503 | ofport_map) { |
42943cde EJ |
504 | if (tnl_port->ofport == ofport) { |
505 | return tnl_port; | |
506 | } | |
507 | } | |
508 | return NULL; | |
509 | } | |
510 | ||
7d1a8e7a | 511 | static struct tnl_port * |
c3fd6901 BP |
512 | tnl_find_exact(struct tnl_match *match, struct hmap *map) |
513 | OVS_REQ_RDLOCK(rwlock) | |
7d1a8e7a | 514 | { |
c3fd6901 BP |
515 | if (map) { |
516 | struct tnl_port *tnl_port; | |
7d1a8e7a | 517 | |
c3fd6901 BP |
518 | HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match), map) { |
519 | if (!memcmp(match, &tnl_port->match, sizeof *match)) { | |
520 | return tnl_port; | |
521 | } | |
7d1a8e7a JG |
522 | } |
523 | } | |
524 | return NULL; | |
525 | } | |
526 | ||
32d0eee8 BP |
527 | /* Returns the tnl_port that is the best match for the tunnel data in 'flow', |
528 | * or NULL if no tnl_port matches 'flow'. */ | |
7d1a8e7a | 529 | static struct tnl_port * |
32d0eee8 | 530 | tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 531 | { |
c3fd6901 BP |
532 | enum ip_src_type ip_src; |
533 | int in_key_flow; | |
534 | int ip_dst_flow; | |
535 | int i; | |
536 | ||
537 | i = 0; | |
538 | for (in_key_flow = 0; in_key_flow < 2; in_key_flow++) { | |
539 | for (ip_dst_flow = 0; ip_dst_flow < 2; ip_dst_flow++) { | |
540 | for (ip_src = 0; ip_src < 3; ip_src++) { | |
541 | struct hmap *map = tnl_match_maps[i]; | |
542 | ||
543 | if (map) { | |
544 | struct tnl_port *tnl_port; | |
545 | struct tnl_match match; | |
546 | ||
547 | memset(&match, 0, sizeof match); | |
548 | ||
549 | /* The apparent mix-up of 'ip_dst' and 'ip_src' below is | |
550 | * correct, because "struct tnl_match" is expressed in | |
551 | * terms of packets being sent out, but we are using it | |
552 | * here as a description of how to treat received | |
553 | * packets. */ | |
554 | match.in_key = in_key_flow ? 0 : flow->tunnel.tun_id; | |
ffe4c74f JB |
555 | if (ip_src == IP_SRC_CFG) { |
556 | match.ipv6_src = flow_tnl_dst(&flow->tunnel); | |
b863fb65 | 557 | } |
ffe4c74f JB |
558 | if (!ip_dst_flow) { |
559 | match.ipv6_dst = flow_tnl_src(&flow->tunnel); | |
b863fb65 | 560 | } |
c3fd6901 | 561 | match.odp_port = flow->in_port.odp_port; |
c3fd6901 BP |
562 | match.in_key_flow = in_key_flow; |
563 | match.ip_dst_flow = ip_dst_flow; | |
564 | match.ip_src_flow = ip_src == IP_SRC_FLOW; | |
565 | ||
566 | tnl_port = tnl_find_exact(&match, map); | |
567 | if (tnl_port) { | |
568 | return tnl_port; | |
569 | } | |
570 | } | |
571 | ||
572 | i++; | |
573 | } | |
32d0eee8 | 574 | } |
0ad90c84 JR |
575 | } |
576 | ||
7d1a8e7a JG |
577 | return NULL; |
578 | } | |
579 | ||
c3fd6901 BP |
580 | /* Returns a pointer to the 'tnl_match_maps' element corresponding to 'm''s |
581 | * matching criteria. */ | |
582 | static struct hmap ** | |
583 | tnl_match_map(const struct tnl_match *m) | |
584 | { | |
585 | enum ip_src_type ip_src; | |
586 | ||
587 | ip_src = (m->ip_src_flow ? IP_SRC_FLOW | |
b863fb65 | 588 | : ipv6_addr_is_set(&m->ipv6_src) ? IP_SRC_CFG |
c3fd6901 BP |
589 | : IP_SRC_ANY); |
590 | ||
591 | return &tnl_match_maps[6 * m->in_key_flow + 3 * m->ip_dst_flow + ip_src]; | |
592 | } | |
593 | ||
7d1a8e7a JG |
594 | static void |
595 | tnl_match_fmt(const struct tnl_match *match, struct ds *ds) | |
fa117360 | 596 | OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 597 | { |
0ad90c84 | 598 | if (!match->ip_dst_flow) { |
ac6d120f | 599 | ipv6_format_mapped(&match->ipv6_src, ds); |
b863fb65 | 600 | ds_put_cstr(ds, "->"); |
ac6d120f | 601 | ipv6_format_mapped(&match->ipv6_dst, ds); |
0ad90c84 | 602 | } else if (!match->ip_src_flow) { |
ac6d120f | 603 | ipv6_format_mapped(&match->ipv6_src, ds); |
b863fb65 | 604 | ds_put_cstr(ds, "->flow"); |
0ad90c84 JR |
605 | } else { |
606 | ds_put_cstr(ds, "flow->flow"); | |
607 | } | |
7d1a8e7a | 608 | |
baa73637 EJ |
609 | if (match->in_key_flow) { |
610 | ds_put_cstr(ds, ", key=flow"); | |
611 | } else { | |
612 | ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); | |
7d1a8e7a JG |
613 | } |
614 | ||
615 | ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port); | |
616 | } | |
617 | ||
618 | static void | |
619 | tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action) | |
fa117360 | 620 | OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a JG |
621 | { |
622 | if (VLOG_IS_DBG_ENABLED()) { | |
623 | struct ds ds = DS_EMPTY_INITIALIZER; | |
624 | ||
625 | tnl_match_fmt(&tnl_port->match, &ds); | |
74a99109 EJ |
626 | VLOG_INFO("%s tunnel port %s (%s)", action, |
627 | tnl_port_get_name(tnl_port), ds_cstr(&ds)); | |
7d1a8e7a JG |
628 | ds_destroy(&ds); |
629 | } | |
630 | } | |
631 | ||
632 | static char * | |
fa117360 | 633 | tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a JG |
634 | { |
635 | const struct netdev_tunnel_config *cfg = | |
e1939dbf | 636 | netdev_get_tunnel_config(tnl_port->netdev); |
7d1a8e7a JG |
637 | struct ds ds = DS_EMPTY_INITIALIZER; |
638 | ||
639 | ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port, | |
640 | tnl_port_get_name(tnl_port), | |
e1939dbf | 641 | netdev_get_type(tnl_port->netdev)); |
7d1a8e7a JG |
642 | tnl_match_fmt(&tnl_port->match, &ds); |
643 | ||
644 | if (cfg->out_key != cfg->in_key || | |
645 | cfg->out_key_present != cfg->in_key_present || | |
646 | cfg->out_key_flow != cfg->in_key_flow) { | |
647 | ds_put_cstr(&ds, ", out_key="); | |
648 | if (!cfg->out_key_present) { | |
649 | ds_put_cstr(&ds, "none"); | |
650 | } else if (cfg->out_key_flow) { | |
651 | ds_put_cstr(&ds, "flow"); | |
652 | } else { | |
653 | ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key)); | |
654 | } | |
655 | } | |
656 | ||
657 | if (cfg->ttl_inherit) { | |
658 | ds_put_cstr(&ds, ", ttl=inherit"); | |
659 | } else { | |
660 | ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl); | |
661 | } | |
662 | ||
663 | if (cfg->tos_inherit) { | |
664 | ds_put_cstr(&ds, ", tos=inherit"); | |
665 | } else if (cfg->tos) { | |
666 | ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos); | |
667 | } | |
668 | ||
669 | if (!cfg->dont_fragment) { | |
670 | ds_put_cstr(&ds, ", df=false"); | |
671 | } | |
672 | ||
673 | if (cfg->csum) { | |
674 | ds_put_cstr(&ds, ", csum=true"); | |
675 | } | |
676 | ||
677 | ds_put_cstr(&ds, ")\n"); | |
678 | ||
679 | return ds_steal_cstr(&ds); | |
680 | } | |
681 | ||
682 | static const char * | |
fa117360 | 683 | tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 684 | { |
e1939dbf | 685 | return netdev_get_name(tnl_port->netdev); |
7d1a8e7a | 686 | } |
a36de779 PS |
687 | |
688 | int | |
689 | tnl_port_build_header(const struct ofport_dpif *ofport, | |
4975aa3e PS |
690 | struct ovs_action_push_tnl *data, |
691 | const struct netdev_tnl_build_header_params *params) | |
a36de779 PS |
692 | { |
693 | struct tnl_port *tnl_port; | |
a36de779 PS |
694 | int res; |
695 | ||
696 | fat_rwlock_rdlock(&rwlock); | |
697 | tnl_port = tnl_find_ofport(ofport); | |
698 | ovs_assert(tnl_port); | |
4975aa3e | 699 | res = netdev_build_header(tnl_port->netdev, data, params); |
a36de779 PS |
700 | fat_rwlock_unlock(&rwlock); |
701 | ||
702 | return res; | |
703 | } |