]>
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 JG |
335 | /* Should be called at the beginning of action translation to initialize |
336 | * wildcards and perform any actions based on receiving on tunnel port. | |
337 | * | |
338 | * Returns false if the packet must be dropped. */ | |
339 | bool | |
227a0141 | 340 | tnl_process_ecn(struct flow *flow) |
62a7cc71 | 341 | { |
227a0141 BP |
342 | if (!tnl_port_should_receive(flow)) { |
343 | return true; | |
344 | } | |
345 | ||
346 | if (is_ip_any(flow) && (flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) { | |
347 | if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) { | |
348 | VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE" | |
349 | " but is not ECN capable"); | |
62a7cc71 JG |
350 | return false; |
351 | } | |
ed2e7245 | 352 | |
227a0141 BP |
353 | /* Set the ECN CE value in the tunneled packet. */ |
354 | flow->nw_tos |= IP_ECN_CE; | |
62a7cc71 JG |
355 | } |
356 | ||
227a0141 | 357 | flow->pkt_mark &= ~IPSEC_MARK; |
62a7cc71 JG |
358 | return true; |
359 | } | |
360 | ||
227a0141 BP |
361 | void |
362 | tnl_wc_init(struct flow *flow, struct flow_wildcards *wc) | |
363 | { | |
364 | if (tnl_port_should_receive(flow)) { | |
365 | wc->masks.tunnel.tun_id = OVS_BE64_MAX; | |
366 | wc->masks.tunnel.ip_src = OVS_BE32_MAX; | |
367 | wc->masks.tunnel.ip_dst = OVS_BE32_MAX; | |
368 | wc->masks.tunnel.flags = (FLOW_TNL_F_DONT_FRAGMENT | | |
369 | FLOW_TNL_F_CSUM | | |
370 | FLOW_TNL_F_KEY); | |
371 | wc->masks.tunnel.ip_tos = UINT8_MAX; | |
372 | wc->masks.tunnel.ip_ttl = UINT8_MAX; | |
373 | /* The tp_src and tp_dst members in flow_tnl are set to be always | |
374 | * wildcarded, not to unwildcard them here. */ | |
375 | wc->masks.tunnel.tp_src = 0; | |
376 | wc->masks.tunnel.tp_dst = 0; | |
377 | ||
378 | memset(&wc->masks.pkt_mark, 0xff, sizeof wc->masks.pkt_mark); | |
379 | ||
380 | if (is_ip_any(flow) | |
381 | && (flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) { | |
382 | wc->masks.nw_tos |= IP_ECN_MASK; | |
383 | } | |
384 | } | |
385 | } | |
386 | ||
7d1a8e7a JG |
387 | /* Given that 'flow' should be output to the ofport corresponding to |
388 | * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath | |
4e022ec0 | 389 | * port that the output should happen on. May return ODPP_NONE if the output |
7d1a8e7a | 390 | * shouldn't occur. */ |
4e022ec0 | 391 | odp_port_t |
42943cde | 392 | tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow, |
fa117360 | 393 | struct flow_wildcards *wc) OVS_EXCLUDED(rwlock) |
7d1a8e7a JG |
394 | { |
395 | const struct netdev_tunnel_config *cfg; | |
fa117360 | 396 | struct tnl_port *tnl_port; |
7d1a8e7a | 397 | char *pre_flow_str = NULL; |
fa117360 | 398 | odp_port_t out_port; |
7d1a8e7a | 399 | |
493823d8 | 400 | fat_rwlock_rdlock(&rwlock); |
fa117360 EJ |
401 | tnl_port = tnl_find_ofport(ofport); |
402 | out_port = tnl_port ? tnl_port->match.odp_port : ODPP_NONE; | |
42943cde | 403 | if (!tnl_port) { |
fa117360 | 404 | goto out; |
7d1a8e7a JG |
405 | } |
406 | ||
e1939dbf | 407 | cfg = netdev_get_tunnel_config(tnl_port->netdev); |
7d1a8e7a JG |
408 | ovs_assert(cfg); |
409 | ||
410 | if (!VLOG_DROP_DBG(&dbg_rl)) { | |
411 | pre_flow_str = flow_to_string(flow); | |
412 | } | |
413 | ||
0ad90c84 JR |
414 | if (!cfg->ip_src_flow) { |
415 | flow->tunnel.ip_src = tnl_port->match.ip_src; | |
416 | } | |
417 | if (!cfg->ip_dst_flow) { | |
418 | flow->tunnel.ip_dst = tnl_port->match.ip_dst; | |
419 | } | |
1362e248 | 420 | flow->pkt_mark = tnl_port->match.pkt_mark; |
7d1a8e7a JG |
421 | |
422 | if (!cfg->out_key_flow) { | |
423 | flow->tunnel.tun_id = cfg->out_key; | |
424 | } | |
425 | ||
426 | if (cfg->ttl_inherit && is_ip_any(flow)) { | |
d4f4a9b2 | 427 | wc->masks.nw_ttl = 0xff; |
7d1a8e7a JG |
428 | flow->tunnel.ip_ttl = flow->nw_ttl; |
429 | } else { | |
430 | flow->tunnel.ip_ttl = cfg->ttl; | |
431 | } | |
432 | ||
433 | if (cfg->tos_inherit && is_ip_any(flow)) { | |
cb85b49c | 434 | wc->masks.nw_tos |= IP_DSCP_MASK; |
7d1a8e7a JG |
435 | flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK; |
436 | } else { | |
437 | flow->tunnel.ip_tos = cfg->tos; | |
438 | } | |
439 | ||
abcd4402 JP |
440 | /* ECN fields are always inherited. */ |
441 | if (is_ip_any(flow)) { | |
442 | wc->masks.nw_tos |= IP_ECN_MASK; | |
abcd4402 | 443 | |
1ac9326d JR |
444 | if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) { |
445 | flow->tunnel.ip_tos |= IP_ECN_ECT_0; | |
446 | } else { | |
447 | flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK; | |
448 | } | |
7d1a8e7a JG |
449 | } |
450 | ||
b666962b | 451 | flow->tunnel.flags |= (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0) |
7d1a8e7a JG |
452 | | (cfg->csum ? FLOW_TNL_F_CSUM : 0) |
453 | | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0); | |
454 | ||
455 | if (pre_flow_str) { | |
456 | char *post_flow_str = flow_to_string(flow); | |
457 | char *tnl_str = tnl_port_fmt(tnl_port); | |
458 | VLOG_DBG("flow sent\n" | |
459 | "%s" | |
460 | " pre: %s\n" | |
461 | "post: %s", | |
462 | tnl_str, pre_flow_str, post_flow_str); | |
463 | free(tnl_str); | |
464 | free(pre_flow_str); | |
465 | free(post_flow_str); | |
466 | } | |
467 | ||
fa117360 | 468 | out: |
493823d8 | 469 | fat_rwlock_unlock(&rwlock); |
fa117360 | 470 | return out_port; |
7d1a8e7a JG |
471 | } |
472 | ||
473 | static uint32_t | |
474 | tnl_hash(struct tnl_match *match) | |
475 | { | |
476 | BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0); | |
477 | return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0); | |
478 | } | |
479 | ||
42943cde | 480 | static struct tnl_port * |
fa117360 | 481 | tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock) |
42943cde EJ |
482 | { |
483 | struct tnl_port *tnl_port; | |
484 | ||
485 | HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0), | |
fa117360 | 486 | ofport_map) { |
42943cde EJ |
487 | if (tnl_port->ofport == ofport) { |
488 | return tnl_port; | |
489 | } | |
490 | } | |
491 | return NULL; | |
492 | } | |
493 | ||
7d1a8e7a | 494 | static struct tnl_port * |
c3fd6901 BP |
495 | tnl_find_exact(struct tnl_match *match, struct hmap *map) |
496 | OVS_REQ_RDLOCK(rwlock) | |
7d1a8e7a | 497 | { |
c3fd6901 BP |
498 | if (map) { |
499 | struct tnl_port *tnl_port; | |
7d1a8e7a | 500 | |
c3fd6901 BP |
501 | HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match), map) { |
502 | if (!memcmp(match, &tnl_port->match, sizeof *match)) { | |
503 | return tnl_port; | |
504 | } | |
7d1a8e7a JG |
505 | } |
506 | } | |
507 | return NULL; | |
508 | } | |
509 | ||
32d0eee8 BP |
510 | /* Returns the tnl_port that is the best match for the tunnel data in 'flow', |
511 | * or NULL if no tnl_port matches 'flow'. */ | |
7d1a8e7a | 512 | static struct tnl_port * |
32d0eee8 | 513 | tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 514 | { |
c3fd6901 BP |
515 | enum ip_src_type ip_src; |
516 | int in_key_flow; | |
517 | int ip_dst_flow; | |
518 | int i; | |
519 | ||
520 | i = 0; | |
521 | for (in_key_flow = 0; in_key_flow < 2; in_key_flow++) { | |
522 | for (ip_dst_flow = 0; ip_dst_flow < 2; ip_dst_flow++) { | |
523 | for (ip_src = 0; ip_src < 3; ip_src++) { | |
524 | struct hmap *map = tnl_match_maps[i]; | |
525 | ||
526 | if (map) { | |
527 | struct tnl_port *tnl_port; | |
528 | struct tnl_match match; | |
529 | ||
530 | memset(&match, 0, sizeof match); | |
531 | ||
532 | /* The apparent mix-up of 'ip_dst' and 'ip_src' below is | |
533 | * correct, because "struct tnl_match" is expressed in | |
534 | * terms of packets being sent out, but we are using it | |
535 | * here as a description of how to treat received | |
536 | * packets. */ | |
537 | match.in_key = in_key_flow ? 0 : flow->tunnel.tun_id; | |
538 | match.ip_src = (ip_src == IP_SRC_CFG | |
539 | ? flow->tunnel.ip_dst | |
540 | : 0); | |
541 | match.ip_dst = ip_dst_flow ? 0 : flow->tunnel.ip_src; | |
542 | match.odp_port = flow->in_port.odp_port; | |
543 | match.pkt_mark = flow->pkt_mark; | |
544 | match.in_key_flow = in_key_flow; | |
545 | match.ip_dst_flow = ip_dst_flow; | |
546 | match.ip_src_flow = ip_src == IP_SRC_FLOW; | |
547 | ||
548 | tnl_port = tnl_find_exact(&match, map); | |
549 | if (tnl_port) { | |
550 | return tnl_port; | |
551 | } | |
552 | } | |
553 | ||
554 | i++; | |
555 | } | |
32d0eee8 | 556 | } |
0ad90c84 JR |
557 | } |
558 | ||
7d1a8e7a JG |
559 | return NULL; |
560 | } | |
561 | ||
c3fd6901 BP |
562 | /* Returns a pointer to the 'tnl_match_maps' element corresponding to 'm''s |
563 | * matching criteria. */ | |
564 | static struct hmap ** | |
565 | tnl_match_map(const struct tnl_match *m) | |
566 | { | |
567 | enum ip_src_type ip_src; | |
568 | ||
569 | ip_src = (m->ip_src_flow ? IP_SRC_FLOW | |
570 | : m->ip_src ? IP_SRC_CFG | |
571 | : IP_SRC_ANY); | |
572 | ||
573 | return &tnl_match_maps[6 * m->in_key_flow + 3 * m->ip_dst_flow + ip_src]; | |
574 | } | |
575 | ||
7d1a8e7a JG |
576 | static void |
577 | tnl_match_fmt(const struct tnl_match *match, struct ds *ds) | |
fa117360 | 578 | OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 579 | { |
0ad90c84 JR |
580 | if (!match->ip_dst_flow) { |
581 | ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src), | |
582 | IP_ARGS(match->ip_dst)); | |
583 | } else if (!match->ip_src_flow) { | |
584 | ds_put_format(ds, IP_FMT"->flow", IP_ARGS(match->ip_src)); | |
585 | } else { | |
586 | ds_put_cstr(ds, "flow->flow"); | |
587 | } | |
7d1a8e7a | 588 | |
baa73637 EJ |
589 | if (match->in_key_flow) { |
590 | ds_put_cstr(ds, ", key=flow"); | |
591 | } else { | |
592 | ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); | |
7d1a8e7a JG |
593 | } |
594 | ||
595 | ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port); | |
1362e248 | 596 | ds_put_format(ds, ", pkt mark=%"PRIu32, match->pkt_mark); |
7d1a8e7a JG |
597 | } |
598 | ||
599 | static void | |
600 | tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action) | |
fa117360 | 601 | OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a JG |
602 | { |
603 | if (VLOG_IS_DBG_ENABLED()) { | |
604 | struct ds ds = DS_EMPTY_INITIALIZER; | |
605 | ||
606 | tnl_match_fmt(&tnl_port->match, &ds); | |
74a99109 EJ |
607 | VLOG_INFO("%s tunnel port %s (%s)", action, |
608 | tnl_port_get_name(tnl_port), ds_cstr(&ds)); | |
7d1a8e7a JG |
609 | ds_destroy(&ds); |
610 | } | |
611 | } | |
612 | ||
613 | static char * | |
fa117360 | 614 | tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a JG |
615 | { |
616 | const struct netdev_tunnel_config *cfg = | |
e1939dbf | 617 | netdev_get_tunnel_config(tnl_port->netdev); |
7d1a8e7a JG |
618 | struct ds ds = DS_EMPTY_INITIALIZER; |
619 | ||
620 | ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port, | |
621 | tnl_port_get_name(tnl_port), | |
e1939dbf | 622 | netdev_get_type(tnl_port->netdev)); |
7d1a8e7a JG |
623 | tnl_match_fmt(&tnl_port->match, &ds); |
624 | ||
625 | if (cfg->out_key != cfg->in_key || | |
626 | cfg->out_key_present != cfg->in_key_present || | |
627 | cfg->out_key_flow != cfg->in_key_flow) { | |
628 | ds_put_cstr(&ds, ", out_key="); | |
629 | if (!cfg->out_key_present) { | |
630 | ds_put_cstr(&ds, "none"); | |
631 | } else if (cfg->out_key_flow) { | |
632 | ds_put_cstr(&ds, "flow"); | |
633 | } else { | |
634 | ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key)); | |
635 | } | |
636 | } | |
637 | ||
638 | if (cfg->ttl_inherit) { | |
639 | ds_put_cstr(&ds, ", ttl=inherit"); | |
640 | } else { | |
641 | ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl); | |
642 | } | |
643 | ||
644 | if (cfg->tos_inherit) { | |
645 | ds_put_cstr(&ds, ", tos=inherit"); | |
646 | } else if (cfg->tos) { | |
647 | ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos); | |
648 | } | |
649 | ||
650 | if (!cfg->dont_fragment) { | |
651 | ds_put_cstr(&ds, ", df=false"); | |
652 | } | |
653 | ||
654 | if (cfg->csum) { | |
655 | ds_put_cstr(&ds, ", csum=true"); | |
656 | } | |
657 | ||
658 | ds_put_cstr(&ds, ")\n"); | |
659 | ||
660 | return ds_steal_cstr(&ds); | |
661 | } | |
662 | ||
663 | static const char * | |
fa117360 | 664 | tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) |
7d1a8e7a | 665 | { |
e1939dbf | 666 | return netdev_get_name(tnl_port->netdev); |
7d1a8e7a | 667 | } |
a36de779 PS |
668 | |
669 | int | |
670 | tnl_port_build_header(const struct ofport_dpif *ofport, | |
671 | const struct flow *tnl_flow, | |
74ff3298 JR |
672 | const struct eth_addr dmac, |
673 | const struct eth_addr smac, | |
a36de779 PS |
674 | ovs_be32 ip_src, struct ovs_action_push_tnl *data) |
675 | { | |
676 | struct tnl_port *tnl_port; | |
677 | struct eth_header *eth; | |
678 | struct ip_header *ip; | |
679 | void *l3; | |
680 | int res; | |
681 | ||
682 | fat_rwlock_rdlock(&rwlock); | |
683 | tnl_port = tnl_find_ofport(ofport); | |
684 | ovs_assert(tnl_port); | |
685 | ||
686 | /* Build Ethernet and IP headers. */ | |
687 | memset(data->header, 0, sizeof data->header); | |
688 | ||
689 | eth = (struct eth_header *)data->header; | |
74ff3298 JR |
690 | eth->eth_dst = dmac; |
691 | eth->eth_src = smac; | |
a36de779 PS |
692 | eth->eth_type = htons(ETH_TYPE_IP); |
693 | ||
694 | l3 = (eth + 1); | |
695 | ip = (struct ip_header *) l3; | |
696 | ||
697 | ip->ip_ihl_ver = IP_IHL_VER(5, 4); | |
698 | ip->ip_tos = tnl_flow->tunnel.ip_tos; | |
699 | ip->ip_ttl = tnl_flow->tunnel.ip_ttl; | |
700 | ip->ip_frag_off = (tnl_flow->tunnel.flags & FLOW_TNL_F_DONT_FRAGMENT) ? | |
701 | htons(IP_DF) : 0; | |
702 | ||
703 | put_16aligned_be32(&ip->ip_src, ip_src); | |
704 | put_16aligned_be32(&ip->ip_dst, tnl_flow->tunnel.ip_dst); | |
705 | ||
c876a4bb | 706 | res = netdev_build_header(tnl_port->netdev, data, tnl_flow); |
a36de779 PS |
707 | ip->ip_csum = csum(ip, sizeof *ip); |
708 | fat_rwlock_unlock(&rwlock); | |
709 | ||
710 | return res; | |
711 | } |