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