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