]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
149f577a | 2 | * Copyright (c) 2008, 2009, 2010 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "in-band.h" | |
19 | #include <arpa/inet.h> | |
20 | #include <errno.h> | |
21 | #include <inttypes.h> | |
22 | #include <net/if.h> | |
23 | #include <string.h> | |
26d9fe3b | 24 | #include <stdlib.h> |
0ad9b732 JP |
25 | #include "dhcp.h" |
26 | #include "dpif.h" | |
064af421 BP |
27 | #include "flow.h" |
28 | #include "mac-learning.h" | |
29 | #include "netdev.h" | |
30 | #include "odp-util.h" | |
31 | #include "ofp-print.h" | |
32 | #include "ofproto.h" | |
33 | #include "ofpbuf.h" | |
34 | #include "openflow/openflow.h" | |
0ad9b732 | 35 | #include "openvswitch/datapath-protocol.h" |
064af421 BP |
36 | #include "packets.h" |
37 | #include "poll-loop.h" | |
38 | #include "rconn.h" | |
39 | #include "status.h" | |
40 | #include "timeval.h" | |
41 | #include "vconn.h" | |
42 | ||
43 | #define THIS_MODULE VLM_in_band | |
44 | #include "vlog.h" | |
45 | ||
85088747 JP |
46 | /* In-band control allows a single network to be used for OpenFlow |
47 | * traffic and other data traffic. Refer to ovs-vswitchd.conf(5) and | |
48 | * secchan(8) for a description of configuring in-band control. | |
49 | * | |
50 | * This comment is an attempt to describe how in-band control works at a | |
51 | * wire- and implementation-level. Correctly implementing in-band | |
52 | * control has proven difficult due to its many subtleties, and has thus | |
53 | * gone through many iterations. Please read through and understand the | |
54 | * reasoning behind the chosen rules before making modifications. | |
55 | * | |
56 | * In Open vSwitch, in-band control is implemented as "hidden" flows (in | |
57 | * that they are not visible through OpenFlow) and at a higher priority | |
d6fbec6d | 58 | * than wildcarded flows can be set up by the controller. This is done |
85088747 JP |
59 | * so that the controller cannot interfere with them and possibly break |
60 | * connectivity with its switches. It is possible to see all flows, | |
61 | * including in-band ones, with the ovs-appctl "bridge/dump-flows" | |
62 | * command. | |
63 | * | |
64 | * The following rules are always enabled with the "normal" action by a | |
65 | * switch with in-band control: | |
66 | * | |
67 | * a. DHCP requests sent from the local port. | |
68 | * b. ARP replies to the local port's MAC address. | |
69 | * c. ARP requests from the local port's MAC address. | |
70 | * d. ARP replies to the remote side's MAC address. Note that the | |
71 | * remote side is either the controller or the gateway to reach | |
72 | * the controller. | |
73 | * e. ARP requests from the remote side's MAC address. Note that | |
74 | * like (d), the MAC is either for the controller or gateway. | |
75 | * f. ARP replies containing the controller's IP address as a target. | |
76 | * g. ARP requests containing the controller's IP address as a source. | |
77 | * h. OpenFlow (6633/tcp) traffic to the controller's IP. | |
78 | * i. OpenFlow (6633/tcp) traffic from the controller's IP. | |
79 | * | |
80 | * The goal of these rules is to be as narrow as possible to allow a | |
81 | * switch to join a network and be able to communicate with a | |
82 | * controller. As mentioned earlier, these rules have higher priority | |
83 | * than the controller's rules, so if they are too broad, they may | |
84 | * prevent the controller from implementing its policy. As such, | |
85 | * in-band actively monitors some aspects of flow and packet processing | |
86 | * so that the rules can be made more precise. | |
87 | * | |
88 | * In-band control monitors attempts to add flows into the datapath that | |
89 | * could interfere with its duties. The datapath only allows exact | |
90 | * match entries, so in-band control is able to be very precise about | |
91 | * the flows it prevents. Flows that miss in the datapath are sent to | |
92 | * userspace to be processed, so preventing these flows from being | |
93 | * cached in the "fast path" does not affect correctness. The only type | |
94 | * of flow that is currently prevented is one that would prevent DHCP | |
95 | * replies from being seen by the local port. For example, a rule that | |
96 | * forwarded all DHCP traffic to the controller would not be allowed, | |
97 | * but one that forwarded to all ports (including the local port) would. | |
98 | * | |
99 | * As mentioned earlier, packets that miss in the datapath are sent to | |
100 | * the userspace for processing. The userspace has its own flow table, | |
101 | * the "classifier", so in-band checks whether any special processing | |
102 | * is needed before the classifier is consulted. If a packet is a DHCP | |
103 | * response to a request from the local port, the packet is forwarded to | |
104 | * the local port, regardless of the flow table. Note that this requires | |
105 | * L7 processing of DHCP replies to determine whether the 'chaddr' field | |
106 | * matches the MAC address of the local port. | |
107 | * | |
108 | * It is interesting to note that for an L3-based in-band control | |
109 | * mechanism, the majority of rules are devoted to ARP traffic. At first | |
110 | * glance, some of these rules appear redundant. However, each serves an | |
111 | * important role. First, in order to determine the MAC address of the | |
112 | * remote side (controller or gateway) for other ARP rules, we must allow | |
113 | * ARP traffic for our local port with rules (b) and (c). If we are | |
114 | * between a switch and its connection to the controller, we have to | |
115 | * allow the other switch's ARP traffic to through. This is done with | |
116 | * rules (d) and (e), since we do not know the addresses of the other | |
117 | * switches a priori, but do know the controller's or gateway's. Finally, | |
118 | * if the controller is running in a local guest VM that is not reached | |
119 | * through the local port, the switch that is connected to the VM must | |
120 | * allow ARP traffic based on the controller's IP address, since it will | |
121 | * not know the MAC address of the local port that is sending the traffic | |
122 | * or the MAC address of the controller in the guest VM. | |
123 | * | |
124 | * With a few notable exceptions below, in-band should work in most | |
125 | * network setups. The following are considered "supported' in the | |
126 | * current implementation: | |
127 | * | |
128 | * - Locally Connected. The switch and controller are on the same | |
129 | * subnet. This uses rules (a), (b), (c), (h), and (i). | |
130 | * | |
131 | * - Reached through Gateway. The switch and controller are on | |
132 | * different subnets and must go through a gateway. This uses | |
133 | * rules (a), (b), (c), (h), and (i). | |
134 | * | |
135 | * - Between Switch and Controller. This switch is between another | |
136 | * switch and the controller, and we want to allow the other | |
137 | * switch's traffic through. This uses rules (d), (e), (h), and | |
138 | * (i). It uses (b) and (c) indirectly in order to know the MAC | |
139 | * address for rules (d) and (e). Note that DHCP for the other | |
140 | * switch will not work unless the controller explicitly lets this | |
141 | * switch pass the traffic. | |
142 | * | |
143 | * - Between Switch and Gateway. This switch is between another | |
144 | * switch and the gateway, and we want to allow the other switch's | |
145 | * traffic through. This uses the same rules and logic as the | |
146 | * "Between Switch and Controller" configuration described earlier. | |
147 | * | |
148 | * - Controller on Local VM. The controller is a guest VM on the | |
149 | * system running in-band control. This uses rules (a), (b), (c), | |
150 | * (h), and (i). | |
151 | * | |
152 | * - Controller on Local VM with Different Networks. The controller | |
153 | * is a guest VM on the system running in-band control, but the | |
154 | * local port is not used to connect to the controller. For | |
155 | * example, an IP address is configured on eth0 of the switch. The | |
156 | * controller's VM is connected through eth1 of the switch, but an | |
157 | * IP address has not been configured for that port on the switch. | |
158 | * As such, the switch will use eth0 to connect to the controller, | |
159 | * and eth1's rules about the local port will not work. In the | |
160 | * example, the switch attached to eth0 would use rules (a), (b), | |
161 | * (c), (h), and (i) on eth0. The switch attached to eth1 would use | |
162 | * rules (f), (g), (h), and (i). | |
163 | * | |
164 | * The following are explicitly *not* supported by in-band control: | |
165 | * | |
166 | * - Specify Controller by Name. Currently, the controller must be | |
167 | * identified by IP address. A naive approach would be to permit | |
168 | * all DNS traffic. Unfortunately, this would prevent the | |
169 | * controller from defining any policy over DNS. Since switches | |
170 | * that are located behind us need to connect to the controller, | |
171 | * in-band cannot simply add a rule that allows DNS traffic from | |
172 | * the local port. The "correct" way to support this is to parse | |
173 | * DNS requests to allow all traffic related to a request for the | |
174 | * controller's name through. Due to the potential security | |
175 | * problems and amount of processing, we decided to hold off for | |
176 | * the time-being. | |
177 | * | |
178 | * - Multiple Controllers. There is nothing intrinsic in the high- | |
179 | * level design that prevents using multiple (known) controllers, | |
180 | * however, the current implementation's data structures assume | |
181 | * only one. | |
182 | * | |
183 | * - Differing Controllers for Switches. All switches must know | |
184 | * the L3 addresses for all the controllers that other switches | |
d6fbec6d | 185 | * may use, since rules need to be set up to allow traffic related |
85088747 JP |
186 | * to those controllers through. See rules (f), (g), (h), and (i). |
187 | * | |
188 | * - Differing Routes for Switches. In order for the switch to | |
189 | * allow other switches to connect to a controller through a | |
190 | * gateway, it allows the gateway's traffic through with rules (d) | |
191 | * and (e). If the routes to the controller differ for the two | |
192 | * switches, we will not know the MAC address of the alternate | |
193 | * gateway. | |
194 | */ | |
195 | ||
064af421 BP |
196 | #define IB_BASE_PRIORITY 18181800 |
197 | ||
198 | enum { | |
85088747 JP |
199 | IBR_FROM_LOCAL_DHCP, /* (a) From local port, DHCP. */ |
200 | IBR_TO_LOCAL_ARP, /* (b) To local port, ARP. */ | |
201 | IBR_FROM_LOCAL_ARP, /* (c) From local port, ARP. */ | |
202 | IBR_TO_REMOTE_ARP, /* (d) To remote MAC, ARP. */ | |
203 | IBR_FROM_REMOTE_ARP, /* (e) From remote MAC, ARP. */ | |
204 | IBR_TO_CTL_ARP, /* (f) To controller IP, ARP. */ | |
205 | IBR_FROM_CTL_ARP, /* (g) From controller IP, ARP. */ | |
206 | IBR_TO_CTL_OFP, /* (h) To controller, OpenFlow port. */ | |
207 | IBR_FROM_CTL_OFP, /* (i) From controller, OpenFlow port. */ | |
064af421 BP |
208 | #if OFP_TCP_PORT != OFP_SSL_PORT |
209 | #error Need to support separate TCP and SSL flows. | |
210 | #endif | |
211 | N_IB_RULES | |
212 | }; | |
213 | ||
214 | struct ib_rule { | |
215 | bool installed; | |
216 | flow_t flow; | |
217 | uint32_t wildcards; | |
218 | unsigned int priority; | |
219 | }; | |
220 | ||
221 | struct in_band { | |
222 | struct ofproto *ofproto; | |
064af421 BP |
223 | struct rconn *controller; |
224 | struct status_category *ss_cat; | |
225 | ||
0ad9b732 JP |
226 | /* Keep track of local port's information. */ |
227 | uint8_t local_mac[ETH_ADDR_LEN]; /* Current MAC. */ | |
f1acd62b | 228 | struct netdev *local_netdev; /* Local port's network device. */ |
0ad9b732 | 229 | time_t next_local_refresh; |
064af421 | 230 | |
0ad9b732 JP |
231 | /* Keep track of controller and next hop's information. */ |
232 | uint32_t controller_ip; /* Controller IP, 0 if unknown. */ | |
233 | uint8_t remote_mac[ETH_ADDR_LEN]; /* Remote MAC. */ | |
f1acd62b | 234 | struct netdev *remote_netdev; |
0ad9b732 JP |
235 | uint8_t last_remote_mac[ETH_ADDR_LEN]; /* Previous remote MAC. */ |
236 | time_t next_remote_refresh; | |
064af421 BP |
237 | |
238 | /* Rules that we set up. */ | |
239 | struct ib_rule rules[N_IB_RULES]; | |
240 | }; | |
241 | ||
242 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); | |
243 | ||
244 | static const uint8_t * | |
0ad9b732 | 245 | get_remote_mac(struct in_band *ib) |
064af421 | 246 | { |
0ad9b732 JP |
247 | int retval; |
248 | bool have_mac; | |
f1acd62b BP |
249 | struct in_addr c_in4; /* Controller's IP address. */ |
250 | struct in_addr r_in4; /* Next hop IP address. */ | |
251 | char *next_hop_dev; | |
064af421 | 252 | time_t now = time_now(); |
064af421 | 253 | |
0ad9b732 | 254 | if (now >= ib->next_remote_refresh) { |
f1acd62b | 255 | /* Find the next-hop IP address. */ |
0ad9b732 JP |
256 | c_in4.s_addr = ib->controller_ip; |
257 | memset(ib->remote_mac, 0, sizeof ib->remote_mac); | |
f1acd62b BP |
258 | retval = netdev_get_next_hop(ib->local_netdev, |
259 | &c_in4, &r_in4, &next_hop_dev); | |
0ad9b732 JP |
260 | if (retval) { |
261 | VLOG_WARN("cannot find route for controller ("IP_FMT"): %s", | |
262 | IP_ARGS(&ib->controller_ip), strerror(retval)); | |
263 | ib->next_remote_refresh = now + 1; | |
264 | return NULL; | |
265 | } | |
266 | if (!r_in4.s_addr) { | |
267 | r_in4.s_addr = c_in4.s_addr; | |
064af421 | 268 | } |
c752217a | 269 | |
f1acd62b BP |
270 | /* Get the next-hop IP and network device. */ |
271 | if (!ib->remote_netdev | |
272 | || strcmp(netdev_get_name(ib->remote_netdev), next_hop_dev)) | |
273 | { | |
274 | netdev_close(ib->remote_netdev); | |
149f577a JG |
275 | |
276 | retval = netdev_open_default(next_hop_dev, &ib->remote_netdev); | |
f1acd62b BP |
277 | if (retval) { |
278 | VLOG_WARN_RL(&rl, "cannot open netdev %s (next hop " | |
279 | "to controller "IP_FMT"): %s", | |
280 | next_hop_dev, IP_ARGS(&ib->controller_ip), | |
281 | strerror(retval)); | |
dc6ddf68 | 282 | free(next_hop_dev); |
f1acd62b BP |
283 | ib->next_remote_refresh = now + 1; |
284 | return NULL; | |
064af421 BP |
285 | } |
286 | } | |
dc6ddf68 | 287 | free(next_hop_dev); |
064af421 | 288 | |
f1acd62b BP |
289 | /* Look up the MAC address of the next-hop IP address. */ |
290 | retval = netdev_arp_lookup(ib->remote_netdev, r_in4.s_addr, | |
291 | ib->remote_mac); | |
0ad9b732 JP |
292 | if (retval) { |
293 | VLOG_DBG_RL(&rl, "cannot look up remote MAC address ("IP_FMT"): %s", | |
294 | IP_ARGS(&r_in4.s_addr), strerror(retval)); | |
064af421 | 295 | } |
0ad9b732 | 296 | have_mac = !eth_addr_is_zero(ib->remote_mac); |
f1acd62b BP |
297 | if (have_mac |
298 | && !eth_addr_equals(ib->last_remote_mac, ib->remote_mac)) { | |
0ad9b732 | 299 | VLOG_DBG("remote MAC address changed from "ETH_ADDR_FMT" to " |
064af421 | 300 | ETH_ADDR_FMT, |
0ad9b732 JP |
301 | ETH_ADDR_ARGS(ib->last_remote_mac), |
302 | ETH_ADDR_ARGS(ib->remote_mac)); | |
303 | memcpy(ib->last_remote_mac, ib->remote_mac, ETH_ADDR_LEN); | |
064af421 BP |
304 | } |
305 | ||
306 | /* Schedule next refresh. | |
307 | * | |
308 | * If we have an IP address but not a MAC address, then refresh | |
309 | * quickly, since we probably will get a MAC address soon (via ARP). | |
310 | * Otherwise, we can afford to wait a little while. */ | |
0ad9b732 JP |
311 | ib->next_remote_refresh |
312 | = now + (!ib->controller_ip || have_mac ? 10 : 1); | |
064af421 | 313 | } |
0ad9b732 JP |
314 | |
315 | return !eth_addr_is_zero(ib->remote_mac) ? ib->remote_mac : NULL; | |
064af421 BP |
316 | } |
317 | ||
318 | static const uint8_t * | |
319 | get_local_mac(struct in_band *ib) | |
320 | { | |
321 | time_t now = time_now(); | |
322 | if (now >= ib->next_local_refresh) { | |
323 | uint8_t ea[ETH_ADDR_LEN]; | |
c0a5fd2a | 324 | if (ib->local_netdev && !netdev_get_etheraddr(ib->local_netdev, ea)) { |
064af421 BP |
325 | memcpy(ib->local_mac, ea, ETH_ADDR_LEN); |
326 | } | |
327 | ib->next_local_refresh = now + 1; | |
328 | } | |
329 | return !eth_addr_is_zero(ib->local_mac) ? ib->local_mac : NULL; | |
330 | } | |
331 | ||
332 | static void | |
333 | in_band_status_cb(struct status_reply *sr, void *in_band_) | |
334 | { | |
335 | struct in_band *in_band = in_band_; | |
064af421 | 336 | |
4701460a | 337 | if (!eth_addr_is_zero(in_band->local_mac)) { |
064af421 | 338 | status_reply_put(sr, "local-mac="ETH_ADDR_FMT, |
4701460a | 339 | ETH_ADDR_ARGS(in_band->local_mac)); |
064af421 BP |
340 | } |
341 | ||
0ad9b732 JP |
342 | if (!eth_addr_is_zero(in_band->remote_mac)) { |
343 | status_reply_put(sr, "remote-mac="ETH_ADDR_FMT, | |
344 | ETH_ADDR_ARGS(in_band->remote_mac)); | |
064af421 BP |
345 | } |
346 | } | |
347 | ||
348 | static void | |
349 | drop_flow(struct in_band *in_band, int rule_idx) | |
350 | { | |
351 | struct ib_rule *rule = &in_band->rules[rule_idx]; | |
352 | ||
353 | if (rule->installed) { | |
354 | rule->installed = false; | |
355 | ofproto_delete_flow(in_band->ofproto, &rule->flow, rule->wildcards, | |
356 | rule->priority); | |
357 | } | |
358 | } | |
359 | ||
360 | /* out_port and fixed_fields are assumed never to change. */ | |
361 | static void | |
d6fbec6d BP |
362 | set_up_flow(struct in_band *in_band, int rule_idx, const flow_t *flow, |
363 | uint32_t fixed_fields, uint16_t out_port) | |
064af421 BP |
364 | { |
365 | struct ib_rule *rule = &in_band->rules[rule_idx]; | |
366 | ||
367 | if (!rule->installed || memcmp(flow, &rule->flow, sizeof *flow)) { | |
368 | union ofp_action action; | |
369 | ||
370 | drop_flow(in_band, rule_idx); | |
371 | ||
372 | rule->installed = true; | |
373 | rule->flow = *flow; | |
659586ef | 374 | rule->wildcards = OVSFW_ALL & ~fixed_fields; |
064af421 BP |
375 | rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx); |
376 | ||
377 | action.type = htons(OFPAT_OUTPUT); | |
378 | action.output.len = htons(sizeof action); | |
379 | action.output.port = htons(out_port); | |
380 | action.output.max_len = htons(0); | |
381 | ofproto_add_flow(in_band->ofproto, &rule->flow, rule->wildcards, | |
382 | rule->priority, &action, 1, 0); | |
383 | } | |
384 | } | |
385 | ||
0ad9b732 JP |
386 | /* Returns true if 'packet' should be sent to the local port regardless |
387 | * of the flow table. */ | |
388 | bool | |
389 | in_band_msg_in_hook(struct in_band *in_band, const flow_t *flow, | |
390 | const struct ofpbuf *packet) | |
391 | { | |
392 | if (!in_band) { | |
393 | return false; | |
394 | } | |
395 | ||
396 | /* Regardless of how the flow table is configured, we want to be | |
397 | * able to see replies to our DHCP requests. */ | |
398 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
399 | && flow->nw_proto == IP_TYPE_UDP | |
400 | && flow->tp_src == htons(DHCP_SERVER_PORT) | |
401 | && flow->tp_dst == htons(DHCP_CLIENT_PORT) | |
402 | && packet->l7) { | |
403 | struct dhcp_header *dhcp; | |
404 | const uint8_t *local_mac; | |
405 | ||
406 | dhcp = ofpbuf_at(packet, (char *)packet->l7 - (char *)packet->data, | |
407 | sizeof *dhcp); | |
408 | if (!dhcp) { | |
409 | return false; | |
410 | } | |
411 | ||
412 | local_mac = get_local_mac(in_band); | |
a3580178 | 413 | if (local_mac && eth_addr_equals(dhcp->chaddr, local_mac)) { |
0ad9b732 JP |
414 | return true; |
415 | } | |
416 | } | |
417 | ||
418 | return false; | |
419 | } | |
420 | ||
421 | /* Returns true if the rule that would match 'flow' with 'actions' is | |
422 | * allowed to be set up in the datapath. */ | |
423 | bool | |
424 | in_band_rule_check(struct in_band *in_band, const flow_t *flow, | |
425 | const struct odp_actions *actions) | |
426 | { | |
427 | if (!in_band) { | |
428 | return true; | |
429 | } | |
430 | ||
431 | /* Don't allow flows that would prevent DHCP replies from being seen | |
432 | * by the local port. */ | |
433 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
434 | && flow->nw_proto == IP_TYPE_UDP | |
435 | && flow->tp_src == htons(DHCP_SERVER_PORT) | |
436 | && flow->tp_dst == htons(DHCP_CLIENT_PORT)) { | |
437 | int i; | |
438 | ||
439 | for (i=0; i<actions->n_actions; i++) { | |
440 | if (actions->actions[i].output.type == ODPAT_OUTPUT | |
441 | && actions->actions[i].output.port == ODPP_LOCAL) { | |
442 | return true; | |
443 | } | |
444 | } | |
445 | return false; | |
446 | } | |
447 | ||
448 | return true; | |
449 | } | |
450 | ||
064af421 BP |
451 | void |
452 | in_band_run(struct in_band *in_band) | |
453 | { | |
0ad9b732 JP |
454 | time_t now = time_now(); |
455 | uint32_t controller_ip; | |
456 | const uint8_t *remote_mac; | |
064af421 BP |
457 | const uint8_t *local_mac; |
458 | flow_t flow; | |
459 | ||
0ad9b732 JP |
460 | if (now < in_band->next_remote_refresh |
461 | && now < in_band->next_local_refresh) { | |
064af421 BP |
462 | return; |
463 | } | |
064af421 | 464 | |
0ad9b732 JP |
465 | controller_ip = rconn_get_remote_ip(in_band->controller); |
466 | if (in_band->controller_ip && controller_ip != in_band->controller_ip) { | |
467 | VLOG_DBG("controller IP address changed from "IP_FMT" to "IP_FMT, | |
468 | IP_ARGS(&in_band->controller_ip), | |
469 | IP_ARGS(&controller_ip)); | |
470 | } | |
471 | in_band->controller_ip = controller_ip; | |
472 | ||
473 | remote_mac = get_remote_mac(in_band); | |
474 | local_mac = get_local_mac(in_band); | |
064af421 | 475 | |
064af421 | 476 | if (local_mac) { |
0ad9b732 | 477 | /* Allow DHCP requests to be sent from the local port. */ |
064af421 | 478 | memset(&flow, 0, sizeof flow); |
0ad9b732 | 479 | flow.in_port = ODPP_LOCAL; |
a5f37a2d | 480 | flow.dl_type = htons(ETH_TYPE_IP); |
0ad9b732 JP |
481 | memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN); |
482 | flow.nw_proto = IP_TYPE_UDP; | |
483 | flow.tp_src = htons(DHCP_CLIENT_PORT); | |
484 | flow.tp_dst = htons(DHCP_SERVER_PORT); | |
d6fbec6d BP |
485 | set_up_flow(in_band, IBR_FROM_LOCAL_DHCP, &flow, |
486 | (OFPFW_IN_PORT | OFPFW_DL_TYPE | OFPFW_DL_SRC | |
487 | | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST), | |
488 | OFPP_NORMAL); | |
0ad9b732 JP |
489 | |
490 | /* Allow the connection's interface to receive directed ARP traffic. */ | |
064af421 | 491 | memset(&flow, 0, sizeof flow); |
0ad9b732 | 492 | flow.dl_type = htons(ETH_TYPE_ARP); |
064af421 | 493 | memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN); |
0ad9b732 | 494 | flow.nw_proto = ARP_OP_REPLY; |
d6fbec6d BP |
495 | set_up_flow(in_band, IBR_TO_LOCAL_ARP, &flow, |
496 | (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), | |
497 | OFPP_NORMAL); | |
26d9fe3b JP |
498 | |
499 | /* Allow the connection's interface to be the source of ARP traffic. */ | |
500 | memset(&flow, 0, sizeof flow); | |
501 | flow.dl_type = htons(ETH_TYPE_ARP); | |
502 | memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN); | |
0ad9b732 | 503 | flow.nw_proto = ARP_OP_REQUEST; |
d6fbec6d BP |
504 | set_up_flow(in_band, IBR_FROM_LOCAL_ARP, &flow, |
505 | (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), | |
506 | OFPP_NORMAL); | |
0ad9b732 | 507 | } else { |
25bfb758 | 508 | drop_flow(in_band, IBR_FROM_LOCAL_DHCP); |
0ad9b732 JP |
509 | drop_flow(in_band, IBR_TO_LOCAL_ARP); |
510 | drop_flow(in_band, IBR_FROM_LOCAL_ARP); | |
511 | } | |
a5f37a2d | 512 | |
0ad9b732 JP |
513 | if (remote_mac) { |
514 | /* Allow ARP replies to the remote side's MAC. */ | |
a5f37a2d JP |
515 | memset(&flow, 0, sizeof flow); |
516 | flow.dl_type = htons(ETH_TYPE_ARP); | |
0ad9b732 JP |
517 | memcpy(flow.dl_dst, remote_mac, ETH_ADDR_LEN); |
518 | flow.nw_proto = ARP_OP_REPLY; | |
d6fbec6d BP |
519 | set_up_flow(in_band, IBR_TO_REMOTE_ARP, &flow, |
520 | (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), | |
521 | OFPP_NORMAL); | |
0ad9b732 JP |
522 | |
523 | /* Allow ARP requests from the remote side's MAC. */ | |
524 | memset(&flow, 0, sizeof flow); | |
525 | flow.dl_type = htons(ETH_TYPE_ARP); | |
526 | memcpy(flow.dl_src, remote_mac, ETH_ADDR_LEN); | |
527 | flow.nw_proto = ARP_OP_REQUEST; | |
d6fbec6d BP |
528 | set_up_flow(in_band, IBR_FROM_REMOTE_ARP, &flow, |
529 | (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), | |
530 | OFPP_NORMAL); | |
064af421 | 531 | } else { |
0ad9b732 JP |
532 | drop_flow(in_band, IBR_TO_REMOTE_ARP); |
533 | drop_flow(in_band, IBR_FROM_REMOTE_ARP); | |
064af421 BP |
534 | } |
535 | ||
0ad9b732 JP |
536 | if (controller_ip) { |
537 | /* Allow ARP replies to the controller's IP. */ | |
064af421 BP |
538 | memset(&flow, 0, sizeof flow); |
539 | flow.dl_type = htons(ETH_TYPE_ARP); | |
0ad9b732 JP |
540 | flow.nw_proto = ARP_OP_REPLY; |
541 | flow.nw_dst = controller_ip; | |
d6fbec6d BP |
542 | set_up_flow(in_band, IBR_TO_CTL_ARP, &flow, |
543 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK), | |
544 | OFPP_NORMAL); | |
064af421 | 545 | |
0ad9b732 JP |
546 | /* Allow ARP requests from the controller's IP. */ |
547 | memset(&flow, 0, sizeof flow); | |
548 | flow.dl_type = htons(ETH_TYPE_ARP); | |
549 | flow.nw_proto = ARP_OP_REQUEST; | |
550 | flow.nw_src = controller_ip; | |
d6fbec6d BP |
551 | set_up_flow(in_band, IBR_FROM_CTL_ARP, &flow, |
552 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK), | |
553 | OFPP_NORMAL); | |
0ad9b732 | 554 | |
064af421 BP |
555 | /* OpenFlow traffic to or from the controller. |
556 | * | |
557 | * (A given field's value is completely ignored if it is wildcarded, | |
558 | * which is why we can get away with using a single 'flow' in each | |
559 | * case here.) */ | |
560 | memset(&flow, 0, sizeof flow); | |
561 | flow.dl_type = htons(ETH_TYPE_IP); | |
064af421 | 562 | flow.nw_proto = IP_TYPE_TCP; |
0ad9b732 JP |
563 | flow.nw_src = controller_ip; |
564 | flow.nw_dst = controller_ip; | |
064af421 BP |
565 | flow.tp_src = htons(OFP_TCP_PORT); |
566 | flow.tp_dst = htons(OFP_TCP_PORT); | |
d6fbec6d BP |
567 | set_up_flow(in_band, IBR_TO_CTL_OFP, &flow, |
568 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK | |
569 | | OFPFW_TP_DST), OFPP_NORMAL); | |
570 | set_up_flow(in_band, IBR_FROM_CTL_OFP, &flow, | |
571 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | |
572 | | OFPFW_TP_SRC), OFPP_NORMAL); | |
064af421 | 573 | } else { |
0ad9b732 JP |
574 | drop_flow(in_band, IBR_TO_CTL_ARP); |
575 | drop_flow(in_band, IBR_FROM_CTL_ARP); | |
576 | drop_flow(in_band, IBR_TO_CTL_OFP); | |
577 | drop_flow(in_band, IBR_FROM_CTL_OFP); | |
064af421 BP |
578 | } |
579 | } | |
580 | ||
581 | void | |
582 | in_band_wait(struct in_band *in_band) | |
583 | { | |
584 | time_t now = time_now(); | |
0ad9b732 JP |
585 | time_t wakeup |
586 | = MIN(in_band->next_remote_refresh, in_band->next_local_refresh); | |
064af421 BP |
587 | if (wakeup > now) { |
588 | poll_timer_wait((wakeup - now) * 1000); | |
589 | } else { | |
590 | poll_immediate_wake(); | |
591 | } | |
592 | } | |
593 | ||
594 | void | |
595 | in_band_flushed(struct in_band *in_band) | |
596 | { | |
597 | int i; | |
598 | ||
599 | for (i = 0; i < N_IB_RULES; i++) { | |
600 | in_band->rules[i].installed = false; | |
601 | } | |
602 | } | |
603 | ||
f1acd62b | 604 | int |
0ad9b732 JP |
605 | in_band_create(struct ofproto *ofproto, struct dpif *dpif, |
606 | struct switch_status *ss, struct rconn *controller, | |
607 | struct in_band **in_bandp) | |
064af421 BP |
608 | { |
609 | struct in_band *in_band; | |
f1acd62b BP |
610 | char local_name[IF_NAMESIZE]; |
611 | struct netdev *local_netdev; | |
0ad9b732 | 612 | int error; |
064af421 | 613 | |
f1acd62b BP |
614 | error = dpif_port_get_name(dpif, ODPP_LOCAL, |
615 | local_name, sizeof local_name); | |
0ad9b732 | 616 | if (error) { |
f1acd62b BP |
617 | VLOG_ERR("failed to initialize in-band control: cannot get name " |
618 | "of datapath local port (%s)", strerror(error)); | |
619 | return error; | |
0ad9b732 JP |
620 | } |
621 | ||
149f577a | 622 | error = netdev_open_default(local_name, &local_netdev); |
f1acd62b BP |
623 | if (error) { |
624 | VLOG_ERR("failed to initialize in-band control: cannot open " | |
625 | "datapath local port %s (%s)", local_name, strerror(error)); | |
626 | return error; | |
627 | } | |
064af421 | 628 | |
ec6fde61 | 629 | in_band = xzalloc(sizeof *in_band); |
064af421 | 630 | in_band->ofproto = ofproto; |
064af421 BP |
631 | in_band->controller = controller; |
632 | in_band->ss_cat = switch_status_register(ss, "in-band", | |
633 | in_band_status_cb, in_band); | |
f1acd62b | 634 | in_band->local_netdev = local_netdev; |
064af421 | 635 | in_band->next_local_refresh = TIME_MIN; |
f1acd62b BP |
636 | in_band->remote_netdev = NULL; |
637 | in_band->next_remote_refresh = TIME_MIN; | |
064af421 BP |
638 | |
639 | *in_bandp = in_band; | |
f1acd62b BP |
640 | |
641 | return 0; | |
064af421 BP |
642 | } |
643 | ||
644 | void | |
645 | in_band_destroy(struct in_band *in_band) | |
646 | { | |
647 | if (in_band) { | |
064af421 | 648 | switch_status_unregister(in_band->ss_cat); |
f1acd62b BP |
649 | netdev_close(in_band->local_netdev); |
650 | netdev_close(in_band->remote_netdev); | |
064af421 | 651 | /* We don't own the rconn. */ |
7103dec4 | 652 | free(in_band); |
064af421 BP |
653 | } |
654 | } | |
655 |