]>
Commit | Line | Data |
---|---|---|
064af421 BP |
1 | /* |
2 | * Copyright (c) 2008, 2009 Nicira Networks. | |
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 | |
58 | * than wildcarded flows can be setup by the controller. This is done | |
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 | |
185 | * may use, since rules need to be setup to allow traffic related | |
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); | |
275 | retval = netdev_open(next_hop_dev, NETDEV_ETH_TYPE_NONE, | |
276 | &ib->remote_netdev); | |
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)); | |
282 | ib->next_remote_refresh = now + 1; | |
283 | return NULL; | |
064af421 BP |
284 | } |
285 | } | |
064af421 | 286 | |
f1acd62b BP |
287 | /* Look up the MAC address of the next-hop IP address. */ |
288 | retval = netdev_arp_lookup(ib->remote_netdev, r_in4.s_addr, | |
289 | ib->remote_mac); | |
0ad9b732 JP |
290 | if (retval) { |
291 | VLOG_DBG_RL(&rl, "cannot look up remote MAC address ("IP_FMT"): %s", | |
292 | IP_ARGS(&r_in4.s_addr), strerror(retval)); | |
064af421 | 293 | } |
0ad9b732 | 294 | have_mac = !eth_addr_is_zero(ib->remote_mac); |
f1acd62b BP |
295 | free(next_hop_dev); |
296 | if (have_mac | |
297 | && !eth_addr_equals(ib->last_remote_mac, ib->remote_mac)) { | |
0ad9b732 | 298 | VLOG_DBG("remote MAC address changed from "ETH_ADDR_FMT" to " |
064af421 | 299 | ETH_ADDR_FMT, |
0ad9b732 JP |
300 | ETH_ADDR_ARGS(ib->last_remote_mac), |
301 | ETH_ADDR_ARGS(ib->remote_mac)); | |
302 | memcpy(ib->last_remote_mac, ib->remote_mac, ETH_ADDR_LEN); | |
064af421 BP |
303 | } |
304 | ||
305 | /* Schedule next refresh. | |
306 | * | |
307 | * If we have an IP address but not a MAC address, then refresh | |
308 | * quickly, since we probably will get a MAC address soon (via ARP). | |
309 | * Otherwise, we can afford to wait a little while. */ | |
0ad9b732 JP |
310 | ib->next_remote_refresh |
311 | = now + (!ib->controller_ip || have_mac ? 10 : 1); | |
064af421 | 312 | } |
0ad9b732 JP |
313 | |
314 | return !eth_addr_is_zero(ib->remote_mac) ? ib->remote_mac : NULL; | |
064af421 BP |
315 | } |
316 | ||
317 | static const uint8_t * | |
318 | get_local_mac(struct in_band *ib) | |
319 | { | |
320 | time_t now = time_now(); | |
321 | if (now >= ib->next_local_refresh) { | |
322 | uint8_t ea[ETH_ADDR_LEN]; | |
c0a5fd2a | 323 | if (ib->local_netdev && !netdev_get_etheraddr(ib->local_netdev, ea)) { |
064af421 BP |
324 | memcpy(ib->local_mac, ea, ETH_ADDR_LEN); |
325 | } | |
326 | ib->next_local_refresh = now + 1; | |
327 | } | |
328 | return !eth_addr_is_zero(ib->local_mac) ? ib->local_mac : NULL; | |
329 | } | |
330 | ||
331 | static void | |
332 | in_band_status_cb(struct status_reply *sr, void *in_band_) | |
333 | { | |
334 | struct in_band *in_band = in_band_; | |
064af421 | 335 | |
4701460a | 336 | if (!eth_addr_is_zero(in_band->local_mac)) { |
064af421 | 337 | status_reply_put(sr, "local-mac="ETH_ADDR_FMT, |
4701460a | 338 | ETH_ADDR_ARGS(in_band->local_mac)); |
064af421 BP |
339 | } |
340 | ||
0ad9b732 JP |
341 | if (!eth_addr_is_zero(in_band->remote_mac)) { |
342 | status_reply_put(sr, "remote-mac="ETH_ADDR_FMT, | |
343 | ETH_ADDR_ARGS(in_band->remote_mac)); | |
064af421 BP |
344 | } |
345 | } | |
346 | ||
347 | static void | |
348 | drop_flow(struct in_band *in_band, int rule_idx) | |
349 | { | |
350 | struct ib_rule *rule = &in_band->rules[rule_idx]; | |
351 | ||
352 | if (rule->installed) { | |
353 | rule->installed = false; | |
354 | ofproto_delete_flow(in_band->ofproto, &rule->flow, rule->wildcards, | |
355 | rule->priority); | |
356 | } | |
357 | } | |
358 | ||
359 | /* out_port and fixed_fields are assumed never to change. */ | |
360 | static void | |
361 | setup_flow(struct in_band *in_band, int rule_idx, const flow_t *flow, | |
362 | uint32_t fixed_fields, uint16_t out_port) | |
363 | { | |
364 | struct ib_rule *rule = &in_band->rules[rule_idx]; | |
365 | ||
366 | if (!rule->installed || memcmp(flow, &rule->flow, sizeof *flow)) { | |
367 | union ofp_action action; | |
368 | ||
369 | drop_flow(in_band, rule_idx); | |
370 | ||
371 | rule->installed = true; | |
372 | rule->flow = *flow; | |
373 | rule->wildcards = OFPFW_ALL & ~fixed_fields; | |
374 | rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx); | |
375 | ||
376 | action.type = htons(OFPAT_OUTPUT); | |
377 | action.output.len = htons(sizeof action); | |
378 | action.output.port = htons(out_port); | |
379 | action.output.max_len = htons(0); | |
380 | ofproto_add_flow(in_band->ofproto, &rule->flow, rule->wildcards, | |
381 | rule->priority, &action, 1, 0); | |
382 | } | |
383 | } | |
384 | ||
0ad9b732 JP |
385 | /* Returns true if 'packet' should be sent to the local port regardless |
386 | * of the flow table. */ | |
387 | bool | |
388 | in_band_msg_in_hook(struct in_band *in_band, const flow_t *flow, | |
389 | const struct ofpbuf *packet) | |
390 | { | |
391 | if (!in_band) { | |
392 | return false; | |
393 | } | |
394 | ||
395 | /* Regardless of how the flow table is configured, we want to be | |
396 | * able to see replies to our DHCP requests. */ | |
397 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
398 | && flow->nw_proto == IP_TYPE_UDP | |
399 | && flow->tp_src == htons(DHCP_SERVER_PORT) | |
400 | && flow->tp_dst == htons(DHCP_CLIENT_PORT) | |
401 | && packet->l7) { | |
402 | struct dhcp_header *dhcp; | |
403 | const uint8_t *local_mac; | |
404 | ||
405 | dhcp = ofpbuf_at(packet, (char *)packet->l7 - (char *)packet->data, | |
406 | sizeof *dhcp); | |
407 | if (!dhcp) { | |
408 | return false; | |
409 | } | |
410 | ||
411 | local_mac = get_local_mac(in_band); | |
412 | if (eth_addr_equals(dhcp->chaddr, local_mac)) { | |
413 | return true; | |
414 | } | |
415 | } | |
416 | ||
417 | return false; | |
418 | } | |
419 | ||
420 | /* Returns true if the rule that would match 'flow' with 'actions' is | |
421 | * allowed to be set up in the datapath. */ | |
422 | bool | |
423 | in_band_rule_check(struct in_band *in_band, const flow_t *flow, | |
424 | const struct odp_actions *actions) | |
425 | { | |
426 | if (!in_band) { | |
427 | return true; | |
428 | } | |
429 | ||
430 | /* Don't allow flows that would prevent DHCP replies from being seen | |
431 | * by the local port. */ | |
432 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
433 | && flow->nw_proto == IP_TYPE_UDP | |
434 | && flow->tp_src == htons(DHCP_SERVER_PORT) | |
435 | && flow->tp_dst == htons(DHCP_CLIENT_PORT)) { | |
436 | int i; | |
437 | ||
438 | for (i=0; i<actions->n_actions; i++) { | |
439 | if (actions->actions[i].output.type == ODPAT_OUTPUT | |
440 | && actions->actions[i].output.port == ODPP_LOCAL) { | |
441 | return true; | |
442 | } | |
443 | } | |
444 | return false; | |
445 | } | |
446 | ||
447 | return true; | |
448 | } | |
449 | ||
064af421 BP |
450 | void |
451 | in_band_run(struct in_band *in_band) | |
452 | { | |
0ad9b732 JP |
453 | time_t now = time_now(); |
454 | uint32_t controller_ip; | |
455 | const uint8_t *remote_mac; | |
064af421 BP |
456 | const uint8_t *local_mac; |
457 | flow_t flow; | |
458 | ||
0ad9b732 JP |
459 | if (now < in_band->next_remote_refresh |
460 | && now < in_band->next_local_refresh) { | |
064af421 BP |
461 | return; |
462 | } | |
064af421 | 463 | |
0ad9b732 JP |
464 | controller_ip = rconn_get_remote_ip(in_band->controller); |
465 | if (in_band->controller_ip && controller_ip != in_band->controller_ip) { | |
466 | VLOG_DBG("controller IP address changed from "IP_FMT" to "IP_FMT, | |
467 | IP_ARGS(&in_band->controller_ip), | |
468 | IP_ARGS(&controller_ip)); | |
469 | } | |
470 | in_band->controller_ip = controller_ip; | |
471 | ||
472 | remote_mac = get_remote_mac(in_band); | |
473 | local_mac = get_local_mac(in_band); | |
064af421 | 474 | |
064af421 | 475 | if (local_mac) { |
0ad9b732 | 476 | /* Allow DHCP requests to be sent from the local port. */ |
064af421 | 477 | memset(&flow, 0, sizeof flow); |
0ad9b732 | 478 | flow.in_port = ODPP_LOCAL; |
a5f37a2d | 479 | flow.dl_type = htons(ETH_TYPE_IP); |
0ad9b732 JP |
480 | memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN); |
481 | flow.nw_proto = IP_TYPE_UDP; | |
482 | flow.tp_src = htons(DHCP_CLIENT_PORT); | |
483 | flow.tp_dst = htons(DHCP_SERVER_PORT); | |
484 | setup_flow(in_band, IBR_FROM_LOCAL_DHCP, &flow, | |
485 | (OFPFW_IN_PORT | OFPFW_DL_TYPE | OFPFW_DL_SRC | |
486 | | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST), | |
487 | OFPP_NORMAL); | |
488 | ||
489 | /* Allow the connection's interface to receive directed ARP traffic. */ | |
064af421 | 490 | memset(&flow, 0, sizeof flow); |
0ad9b732 | 491 | flow.dl_type = htons(ETH_TYPE_ARP); |
064af421 | 492 | memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN); |
0ad9b732 JP |
493 | flow.nw_proto = ARP_OP_REPLY; |
494 | setup_flow(in_band, IBR_TO_LOCAL_ARP, &flow, | |
495 | (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), | |
496 | OFPP_NORMAL); | |
26d9fe3b JP |
497 | |
498 | /* Allow the connection's interface to be the source of ARP traffic. */ | |
499 | memset(&flow, 0, sizeof flow); | |
500 | flow.dl_type = htons(ETH_TYPE_ARP); | |
501 | memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN); | |
0ad9b732 JP |
502 | flow.nw_proto = ARP_OP_REQUEST; |
503 | setup_flow(in_band, IBR_FROM_LOCAL_ARP, &flow, | |
504 | (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), | |
505 | OFPP_NORMAL); | |
506 | } else { | |
507 | drop_flow(in_band, IBR_TO_LOCAL_ARP); | |
508 | drop_flow(in_band, IBR_FROM_LOCAL_ARP); | |
509 | } | |
a5f37a2d | 510 | |
0ad9b732 JP |
511 | if (remote_mac) { |
512 | /* Allow ARP replies to the remote side's MAC. */ | |
a5f37a2d JP |
513 | memset(&flow, 0, sizeof flow); |
514 | flow.dl_type = htons(ETH_TYPE_ARP); | |
0ad9b732 JP |
515 | memcpy(flow.dl_dst, remote_mac, ETH_ADDR_LEN); |
516 | flow.nw_proto = ARP_OP_REPLY; | |
517 | setup_flow(in_band, IBR_TO_REMOTE_ARP, &flow, | |
518 | (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), | |
519 | OFPP_NORMAL); | |
520 | ||
521 | /* Allow ARP requests from the remote side's MAC. */ | |
522 | memset(&flow, 0, sizeof flow); | |
523 | flow.dl_type = htons(ETH_TYPE_ARP); | |
524 | memcpy(flow.dl_src, remote_mac, ETH_ADDR_LEN); | |
525 | flow.nw_proto = ARP_OP_REQUEST; | |
526 | setup_flow(in_band, IBR_FROM_REMOTE_ARP, &flow, | |
527 | (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), | |
528 | OFPP_NORMAL); | |
064af421 | 529 | } else { |
0ad9b732 JP |
530 | drop_flow(in_band, IBR_TO_REMOTE_ARP); |
531 | drop_flow(in_band, IBR_FROM_REMOTE_ARP); | |
064af421 BP |
532 | } |
533 | ||
0ad9b732 JP |
534 | if (controller_ip) { |
535 | /* Allow ARP replies to the controller's IP. */ | |
064af421 BP |
536 | memset(&flow, 0, sizeof flow); |
537 | flow.dl_type = htons(ETH_TYPE_ARP); | |
0ad9b732 JP |
538 | flow.nw_proto = ARP_OP_REPLY; |
539 | flow.nw_dst = controller_ip; | |
540 | setup_flow(in_band, IBR_TO_CTL_ARP, &flow, | |
541 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK), | |
064af421 BP |
542 | OFPP_NORMAL); |
543 | ||
0ad9b732 JP |
544 | /* Allow ARP requests from the controller's IP. */ |
545 | memset(&flow, 0, sizeof flow); | |
546 | flow.dl_type = htons(ETH_TYPE_ARP); | |
547 | flow.nw_proto = ARP_OP_REQUEST; | |
548 | flow.nw_src = controller_ip; | |
549 | setup_flow(in_band, IBR_FROM_CTL_ARP, &flow, | |
550 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK), | |
551 | OFPP_NORMAL); | |
552 | ||
064af421 BP |
553 | /* OpenFlow traffic to or from the controller. |
554 | * | |
555 | * (A given field's value is completely ignored if it is wildcarded, | |
556 | * which is why we can get away with using a single 'flow' in each | |
557 | * case here.) */ | |
558 | memset(&flow, 0, sizeof flow); | |
559 | flow.dl_type = htons(ETH_TYPE_IP); | |
064af421 | 560 | flow.nw_proto = IP_TYPE_TCP; |
0ad9b732 JP |
561 | flow.nw_src = controller_ip; |
562 | flow.nw_dst = controller_ip; | |
064af421 BP |
563 | flow.tp_src = htons(OFP_TCP_PORT); |
564 | flow.tp_dst = htons(OFP_TCP_PORT); | |
0ad9b732 JP |
565 | setup_flow(in_band, IBR_TO_CTL_OFP, &flow, |
566 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK | |
064af421 | 567 | | OFPFW_TP_DST), OFPP_NORMAL); |
0ad9b732 JP |
568 | setup_flow(in_band, IBR_FROM_CTL_OFP, &flow, |
569 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | |
064af421 | 570 | | OFPFW_TP_SRC), OFPP_NORMAL); |
064af421 | 571 | } else { |
0ad9b732 JP |
572 | drop_flow(in_band, IBR_TO_CTL_ARP); |
573 | drop_flow(in_band, IBR_FROM_CTL_ARP); | |
574 | drop_flow(in_band, IBR_TO_CTL_OFP); | |
575 | drop_flow(in_band, IBR_FROM_CTL_OFP); | |
064af421 BP |
576 | } |
577 | } | |
578 | ||
579 | void | |
580 | in_band_wait(struct in_band *in_band) | |
581 | { | |
582 | time_t now = time_now(); | |
0ad9b732 JP |
583 | time_t wakeup |
584 | = MIN(in_band->next_remote_refresh, in_band->next_local_refresh); | |
064af421 BP |
585 | if (wakeup > now) { |
586 | poll_timer_wait((wakeup - now) * 1000); | |
587 | } else { | |
588 | poll_immediate_wake(); | |
589 | } | |
590 | } | |
591 | ||
592 | void | |
593 | in_band_flushed(struct in_band *in_band) | |
594 | { | |
595 | int i; | |
596 | ||
597 | for (i = 0; i < N_IB_RULES; i++) { | |
598 | in_band->rules[i].installed = false; | |
599 | } | |
600 | } | |
601 | ||
f1acd62b | 602 | int |
0ad9b732 JP |
603 | in_band_create(struct ofproto *ofproto, struct dpif *dpif, |
604 | struct switch_status *ss, struct rconn *controller, | |
605 | struct in_band **in_bandp) | |
064af421 BP |
606 | { |
607 | struct in_band *in_band; | |
f1acd62b BP |
608 | char local_name[IF_NAMESIZE]; |
609 | struct netdev *local_netdev; | |
0ad9b732 | 610 | int error; |
064af421 | 611 | |
f1acd62b BP |
612 | error = dpif_port_get_name(dpif, ODPP_LOCAL, |
613 | local_name, sizeof local_name); | |
0ad9b732 | 614 | if (error) { |
f1acd62b BP |
615 | VLOG_ERR("failed to initialize in-band control: cannot get name " |
616 | "of datapath local port (%s)", strerror(error)); | |
617 | return error; | |
0ad9b732 JP |
618 | } |
619 | ||
f1acd62b BP |
620 | error = netdev_open(local_name, NETDEV_ETH_TYPE_NONE, &local_netdev); |
621 | if (error) { | |
622 | VLOG_ERR("failed to initialize in-band control: cannot open " | |
623 | "datapath local port %s (%s)", local_name, strerror(error)); | |
624 | return error; | |
625 | } | |
064af421 BP |
626 | |
627 | in_band = xcalloc(1, sizeof *in_band); | |
628 | in_band->ofproto = ofproto; | |
064af421 BP |
629 | in_band->controller = controller; |
630 | in_band->ss_cat = switch_status_register(ss, "in-band", | |
631 | in_band_status_cb, in_band); | |
f1acd62b | 632 | in_band->local_netdev = local_netdev; |
064af421 | 633 | in_band->next_local_refresh = TIME_MIN; |
f1acd62b BP |
634 | in_band->remote_netdev = NULL; |
635 | in_band->next_remote_refresh = TIME_MIN; | |
064af421 BP |
636 | |
637 | *in_bandp = in_band; | |
f1acd62b BP |
638 | |
639 | return 0; | |
064af421 BP |
640 | } |
641 | ||
642 | void | |
643 | in_band_destroy(struct in_band *in_band) | |
644 | { | |
645 | if (in_band) { | |
064af421 | 646 | switch_status_unregister(in_band->ss_cat); |
f1acd62b BP |
647 | netdev_close(in_band->local_netdev); |
648 | netdev_close(in_band->remote_netdev); | |
064af421 BP |
649 | /* We don't own the rconn. */ |
650 | } | |
651 | } | |
652 |