]>
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 | ||
7d1a8e7a | 477 | if (pre_flow_str) { |
50f96b10 | 478 | char *post_flow_str = flow_to_string(flow, NULL); |
14f602b8 | 479 | char *tnl_str = tnl_port_to_string(tnl_port); |
7d1a8e7a JG |
480 | VLOG_DBG("flow sent\n" |
481 | "%s" | |
482 | " pre: %s\n" | |
483 | "post: %s", | |
484 | tnl_str, pre_flow_str, post_flow_str); | |
485 | free(tnl_str); | |
486 | free(pre_flow_str); | |
487 | free(post_flow_str); | |
488 | } | |
489 | ||
fa117360 | 490 | out: |
493823d8 | 491 | fat_rwlock_unlock(&rwlock); |
fa117360 | 492 | return out_port; |
7d1a8e7a JG |
493 | } |
494 | ||
495 | static uint32_t | |
496 | tnl_hash(struct tnl_match *match) | |
497 | { | |
498 | BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0); | |
499 | return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0); | |
500 | } | |
501 | ||
42943cde | 502 | static struct tnl_port * |
fa117360 | 503 | tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock) |
42943cde EJ |
504 | { |
505 | struct tnl_port *tnl_port; | |
506 | ||
507 | HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0), | |
fa117360 | 508 | ofport_map) { |
42943cde EJ |
509 | if (tnl_port->ofport == ofport) { |
510 | return tnl_port; | |
511 | } | |
512 | } | |
513 | return NULL; | |
514 | } | |
515 | ||
7d1a8e7a | 516 | static struct tnl_port * |
c3fd6901 BP |
517 | tnl_find_exact(struct tnl_match *match, struct hmap *map) |
518 | OVS_REQ_RDLOCK(rwlock) | |
7d1a8e7a | 519 | { |
c3fd6901 BP |
520 | if (map) { |
521 | struct tnl_port *tnl_port; | |
7d1a8e7a | 522 | |
c3fd6901 BP |
523 | HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match), map) { |
524 | if (!memcmp(match, &tnl_port->match, sizeof *match)) { | |
525 | return tnl_port; | |
526 | } | |
7d1a8e7a JG |
527 | } |
528 | } | |
529 | return NULL; | |
530 | } | |
531 | ||
32d0eee8 BP |
532 | /* Returns the tnl_port that is the best match for the tunnel data in 'flow', |
533 | * or NULL if no tnl_port matches 'flow'. */ | |
7d1a8e7a | 534 | static struct tnl_port * |
32d0eee8 | 535 | tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 536 | { |
c3fd6901 BP |
537 | enum ip_src_type ip_src; |
538 | int in_key_flow; | |
539 | int ip_dst_flow; | |
540 | int i; | |
541 | ||
542 | i = 0; | |
543 | for (in_key_flow = 0; in_key_flow < 2; in_key_flow++) { | |
544 | for (ip_dst_flow = 0; ip_dst_flow < 2; ip_dst_flow++) { | |
545 | for (ip_src = 0; ip_src < 3; ip_src++) { | |
546 | struct hmap *map = tnl_match_maps[i]; | |
547 | ||
548 | if (map) { | |
549 | struct tnl_port *tnl_port; | |
550 | struct tnl_match match; | |
551 | ||
552 | memset(&match, 0, sizeof match); | |
553 | ||
554 | /* The apparent mix-up of 'ip_dst' and 'ip_src' below is | |
555 | * correct, because "struct tnl_match" is expressed in | |
556 | * terms of packets being sent out, but we are using it | |
557 | * here as a description of how to treat received | |
558 | * packets. */ | |
559 | match.in_key = in_key_flow ? 0 : flow->tunnel.tun_id; | |
ffe4c74f JB |
560 | if (ip_src == IP_SRC_CFG) { |
561 | match.ipv6_src = flow_tnl_dst(&flow->tunnel); | |
b863fb65 | 562 | } |
ffe4c74f JB |
563 | if (!ip_dst_flow) { |
564 | match.ipv6_dst = flow_tnl_src(&flow->tunnel); | |
b863fb65 | 565 | } |
c3fd6901 | 566 | match.odp_port = flow->in_port.odp_port; |
c3fd6901 BP |
567 | match.in_key_flow = in_key_flow; |
568 | match.ip_dst_flow = ip_dst_flow; | |
569 | match.ip_src_flow = ip_src == IP_SRC_FLOW; | |
570 | ||
875ab130 BP |
571 | /* Look for a legacy L2 or L3 tunnel port first. */ |
572 | if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) { | |
573 | match.pt_mode = NETDEV_PT_LEGACY_L3; | |
574 | } else { | |
575 | match.pt_mode = NETDEV_PT_LEGACY_L2; | |
576 | } | |
577 | tnl_port = tnl_find_exact(&match, map); | |
578 | if (tnl_port) { | |
579 | return tnl_port; | |
580 | } | |
581 | ||
582 | /* Then check for a packet type aware port. */ | |
583 | match.pt_mode = NETDEV_PT_AWARE; | |
c3fd6901 BP |
584 | tnl_port = tnl_find_exact(&match, map); |
585 | if (tnl_port) { | |
586 | return tnl_port; | |
587 | } | |
588 | } | |
589 | ||
590 | i++; | |
591 | } | |
32d0eee8 | 592 | } |
0ad90c84 JR |
593 | } |
594 | ||
7d1a8e7a JG |
595 | return NULL; |
596 | } | |
597 | ||
c3fd6901 BP |
598 | /* Returns a pointer to the 'tnl_match_maps' element corresponding to 'm''s |
599 | * matching criteria. */ | |
600 | static struct hmap ** | |
601 | tnl_match_map(const struct tnl_match *m) | |
602 | { | |
603 | enum ip_src_type ip_src; | |
604 | ||
605 | ip_src = (m->ip_src_flow ? IP_SRC_FLOW | |
b863fb65 | 606 | : ipv6_addr_is_set(&m->ipv6_src) ? IP_SRC_CFG |
c3fd6901 BP |
607 | : IP_SRC_ANY); |
608 | ||
609 | return &tnl_match_maps[6 * m->in_key_flow + 3 * m->ip_dst_flow + ip_src]; | |
610 | } | |
611 | ||
7d1a8e7a JG |
612 | static void |
613 | tnl_match_fmt(const struct tnl_match *match, struct ds *ds) | |
fa117360 | 614 | OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 615 | { |
0ad90c84 | 616 | if (!match->ip_dst_flow) { |
ac6d120f | 617 | ipv6_format_mapped(&match->ipv6_src, ds); |
b863fb65 | 618 | ds_put_cstr(ds, "->"); |
ac6d120f | 619 | ipv6_format_mapped(&match->ipv6_dst, ds); |
0ad90c84 | 620 | } else if (!match->ip_src_flow) { |
ac6d120f | 621 | ipv6_format_mapped(&match->ipv6_src, ds); |
b863fb65 | 622 | ds_put_cstr(ds, "->flow"); |
0ad90c84 JR |
623 | } else { |
624 | ds_put_cstr(ds, "flow->flow"); | |
625 | } | |
7d1a8e7a | 626 | |
baa73637 EJ |
627 | if (match->in_key_flow) { |
628 | ds_put_cstr(ds, ", key=flow"); | |
629 | } else { | |
630 | ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); | |
7d1a8e7a JG |
631 | } |
632 | ||
875ab130 BP |
633 | const char *pt_mode |
634 | = (match->pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" | |
635 | : match->pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" | |
636 | : "ptap"); | |
637 | ds_put_format(ds, ", %s, dp port=%"PRIu32, pt_mode, match->odp_port); | |
7d1a8e7a JG |
638 | } |
639 | ||
640 | static void | |
641 | tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action) | |
fa117360 | 642 | OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a JG |
643 | { |
644 | if (VLOG_IS_DBG_ENABLED()) { | |
645 | struct ds ds = DS_EMPTY_INITIALIZER; | |
646 | ||
647 | tnl_match_fmt(&tnl_port->match, &ds); | |
74a99109 EJ |
648 | VLOG_INFO("%s tunnel port %s (%s)", action, |
649 | tnl_port_get_name(tnl_port), ds_cstr(&ds)); | |
7d1a8e7a JG |
650 | ds_destroy(&ds); |
651 | } | |
652 | } | |
653 | ||
14f602b8 BP |
654 | static void OVS_REQ_RDLOCK(rwlock) |
655 | tnl_port_format(const struct tnl_port *tnl_port, struct ds *ds) | |
7d1a8e7a JG |
656 | { |
657 | const struct netdev_tunnel_config *cfg = | |
e1939dbf | 658 | netdev_get_tunnel_config(tnl_port->netdev); |
7d1a8e7a | 659 | |
14f602b8 | 660 | ds_put_format(ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port, |
7d1a8e7a | 661 | tnl_port_get_name(tnl_port), |
e1939dbf | 662 | netdev_get_type(tnl_port->netdev)); |
14f602b8 | 663 | tnl_match_fmt(&tnl_port->match, ds); |
7d1a8e7a JG |
664 | |
665 | if (cfg->out_key != cfg->in_key || | |
666 | cfg->out_key_present != cfg->in_key_present || | |
667 | cfg->out_key_flow != cfg->in_key_flow) { | |
14f602b8 | 668 | ds_put_cstr(ds, ", out_key="); |
7d1a8e7a | 669 | if (!cfg->out_key_present) { |
14f602b8 | 670 | ds_put_cstr(ds, "none"); |
7d1a8e7a | 671 | } else if (cfg->out_key_flow) { |
14f602b8 | 672 | ds_put_cstr(ds, "flow"); |
7d1a8e7a | 673 | } else { |
14f602b8 | 674 | ds_put_format(ds, "%#"PRIx64, ntohll(cfg->out_key)); |
7d1a8e7a JG |
675 | } |
676 | } | |
677 | ||
678 | if (cfg->ttl_inherit) { | |
14f602b8 | 679 | ds_put_cstr(ds, ", ttl=inherit"); |
7d1a8e7a | 680 | } else { |
14f602b8 | 681 | ds_put_format(ds, ", ttl=%"PRIu8, cfg->ttl); |
7d1a8e7a JG |
682 | } |
683 | ||
684 | if (cfg->tos_inherit) { | |
14f602b8 | 685 | ds_put_cstr(ds, ", tos=inherit"); |
7d1a8e7a | 686 | } else if (cfg->tos) { |
14f602b8 | 687 | ds_put_format(ds, ", tos=%#"PRIx8, cfg->tos); |
7d1a8e7a JG |
688 | } |
689 | ||
690 | if (!cfg->dont_fragment) { | |
14f602b8 | 691 | ds_put_cstr(ds, ", df=false"); |
7d1a8e7a JG |
692 | } |
693 | ||
694 | if (cfg->csum) { | |
14f602b8 | 695 | ds_put_cstr(ds, ", csum=true"); |
7d1a8e7a JG |
696 | } |
697 | ||
14f602b8 BP |
698 | ds_put_cstr(ds, ")\n"); |
699 | } | |
7d1a8e7a | 700 | |
14f602b8 BP |
701 | static char * OVS_REQ_RDLOCK(rwlock) |
702 | tnl_port_to_string(const struct tnl_port *tnl_port) | |
703 | { | |
704 | struct ds ds = DS_EMPTY_INITIALIZER; | |
705 | tnl_port_format(tnl_port, &ds); | |
7d1a8e7a JG |
706 | return ds_steal_cstr(&ds); |
707 | } | |
708 | ||
709 | static const char * | |
fa117360 | 710 | tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 711 | { |
e1939dbf | 712 | return netdev_get_name(tnl_port->netdev); |
7d1a8e7a | 713 | } |
a36de779 PS |
714 | |
715 | int | |
716 | tnl_port_build_header(const struct ofport_dpif *ofport, | |
4975aa3e PS |
717 | struct ovs_action_push_tnl *data, |
718 | const struct netdev_tnl_build_header_params *params) | |
a36de779 PS |
719 | { |
720 | struct tnl_port *tnl_port; | |
a36de779 PS |
721 | int res; |
722 | ||
723 | fat_rwlock_rdlock(&rwlock); | |
724 | tnl_port = tnl_find_ofport(ofport); | |
725 | ovs_assert(tnl_port); | |
4975aa3e | 726 | res = netdev_build_header(tnl_port->netdev, data, params); |
a36de779 PS |
727 | fat_rwlock_unlock(&rwlock); |
728 | ||
729 | return res; | |
730 | } | |
14f602b8 BP |
731 | |
732 | static void | |
733 | tnl_unixctl_list(struct unixctl_conn *conn, | |
734 | int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, | |
735 | void *aux OVS_UNUSED) | |
736 | { | |
737 | struct ds reply = DS_EMPTY_INITIALIZER; | |
738 | ||
739 | fat_rwlock_rdlock(&rwlock); | |
740 | for (int i = 0; i < N_MATCH_TYPES; i++) { | |
741 | struct hmap *map = tnl_match_maps[i]; | |
742 | if (map) { | |
743 | struct tnl_port *tnl_port; | |
744 | HMAP_FOR_EACH (tnl_port, match_node, map) { | |
745 | tnl_port_format(tnl_port, &reply); | |
746 | } | |
747 | } | |
748 | } | |
749 | fat_rwlock_unlock(&rwlock); | |
750 | ||
751 | unixctl_command_reply(conn, ds_cstr(&reply)); | |
752 | ds_destroy(&reply); | |
753 | } |