]>
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 | ||
46 | #define IB_BASE_PRIORITY 18181800 | |
47 | ||
48 | enum { | |
0ad9b732 JP |
49 | IBR_FROM_LOCAL_DHCP, /* From local port, DHCP. */ |
50 | IBR_TO_LOCAL_ARP, /* To local port, ARP. */ | |
51 | IBR_FROM_LOCAL_ARP, /* From local port, ARP. */ | |
52 | IBR_TO_REMOTE_ARP, /* To remote MAC, ARP. */ | |
53 | IBR_FROM_REMOTE_ARP, /* From remote MAC, ARP. */ | |
54 | IBR_TO_CTL_ARP, /* To controller IP, ARP. */ | |
55 | IBR_FROM_CTL_ARP, /* From controller IP, ARP. */ | |
56 | IBR_TO_CTL_OFP, /* To controller, OpenFlow port. */ | |
57 | IBR_FROM_CTL_OFP, /* From controller, OpenFlow port. */ | |
064af421 BP |
58 | #if OFP_TCP_PORT != OFP_SSL_PORT |
59 | #error Need to support separate TCP and SSL flows. | |
60 | #endif | |
61 | N_IB_RULES | |
62 | }; | |
63 | ||
64 | struct ib_rule { | |
65 | bool installed; | |
66 | flow_t flow; | |
67 | uint32_t wildcards; | |
68 | unsigned int priority; | |
69 | }; | |
70 | ||
71 | struct in_band { | |
72 | struct ofproto *ofproto; | |
064af421 BP |
73 | struct rconn *controller; |
74 | struct status_category *ss_cat; | |
75 | ||
0ad9b732 JP |
76 | /* Keep track of local port's information. */ |
77 | uint8_t local_mac[ETH_ADDR_LEN]; /* Current MAC. */ | |
78 | char local_name[IF_NAMESIZE]; /* Local device name. */ | |
79 | time_t next_local_refresh; | |
064af421 | 80 | |
0ad9b732 JP |
81 | /* Keep track of controller and next hop's information. */ |
82 | uint32_t controller_ip; /* Controller IP, 0 if unknown. */ | |
83 | uint8_t remote_mac[ETH_ADDR_LEN]; /* Remote MAC. */ | |
84 | uint8_t last_remote_mac[ETH_ADDR_LEN]; /* Previous remote MAC. */ | |
85 | time_t next_remote_refresh; | |
064af421 BP |
86 | |
87 | /* Rules that we set up. */ | |
88 | struct ib_rule rules[N_IB_RULES]; | |
89 | }; | |
90 | ||
91 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); | |
92 | ||
93 | static const uint8_t * | |
0ad9b732 | 94 | get_remote_mac(struct in_band *ib) |
064af421 | 95 | { |
0ad9b732 JP |
96 | int retval; |
97 | bool have_mac; | |
98 | struct in_addr c_in4, r_in4; | |
99 | char *dev_name; | |
064af421 | 100 | time_t now = time_now(); |
064af421 | 101 | |
0ad9b732 JP |
102 | if (now >= ib->next_remote_refresh) { |
103 | c_in4.s_addr = ib->controller_ip; | |
104 | memset(ib->remote_mac, 0, sizeof ib->remote_mac); | |
105 | retval = netdev_get_next_hop(&c_in4, &r_in4, &dev_name); | |
106 | if (retval) { | |
107 | VLOG_WARN("cannot find route for controller ("IP_FMT"): %s", | |
108 | IP_ARGS(&ib->controller_ip), strerror(retval)); | |
109 | ib->next_remote_refresh = now + 1; | |
110 | return NULL; | |
111 | } | |
112 | if (!r_in4.s_addr) { | |
113 | r_in4.s_addr = c_in4.s_addr; | |
064af421 | 114 | } |
064af421 | 115 | |
0ad9b732 JP |
116 | retval = netdev_nodev_arp_lookup(dev_name, r_in4.s_addr, |
117 | ib->remote_mac); | |
118 | if (retval) { | |
119 | VLOG_DBG_RL(&rl, "cannot look up remote MAC address ("IP_FMT"): %s", | |
120 | IP_ARGS(&r_in4.s_addr), strerror(retval)); | |
064af421 | 121 | } |
0ad9b732 JP |
122 | have_mac = !eth_addr_is_zero(ib->remote_mac); |
123 | free(dev_name); | |
124 | ||
125 | if (have_mac | |
126 | && !eth_addr_equals(ib->last_remote_mac, ib->remote_mac)) { | |
127 | VLOG_DBG("remote MAC address changed from "ETH_ADDR_FMT" to " | |
064af421 | 128 | ETH_ADDR_FMT, |
0ad9b732 JP |
129 | ETH_ADDR_ARGS(ib->last_remote_mac), |
130 | ETH_ADDR_ARGS(ib->remote_mac)); | |
131 | memcpy(ib->last_remote_mac, ib->remote_mac, ETH_ADDR_LEN); | |
064af421 BP |
132 | } |
133 | ||
134 | /* Schedule next refresh. | |
135 | * | |
136 | * If we have an IP address but not a MAC address, then refresh | |
137 | * quickly, since we probably will get a MAC address soon (via ARP). | |
138 | * Otherwise, we can afford to wait a little while. */ | |
0ad9b732 JP |
139 | ib->next_remote_refresh |
140 | = now + (!ib->controller_ip || have_mac ? 10 : 1); | |
064af421 | 141 | } |
0ad9b732 JP |
142 | |
143 | return !eth_addr_is_zero(ib->remote_mac) ? ib->remote_mac : NULL; | |
064af421 BP |
144 | } |
145 | ||
146 | static const uint8_t * | |
147 | get_local_mac(struct in_band *ib) | |
148 | { | |
149 | time_t now = time_now(); | |
150 | if (now >= ib->next_local_refresh) { | |
151 | uint8_t ea[ETH_ADDR_LEN]; | |
0ad9b732 | 152 | if (!netdev_nodev_get_etheraddr(ib->local_name, ea)) { |
064af421 BP |
153 | memcpy(ib->local_mac, ea, ETH_ADDR_LEN); |
154 | } | |
155 | ib->next_local_refresh = now + 1; | |
156 | } | |
157 | return !eth_addr_is_zero(ib->local_mac) ? ib->local_mac : NULL; | |
158 | } | |
159 | ||
160 | static void | |
161 | in_band_status_cb(struct status_reply *sr, void *in_band_) | |
162 | { | |
163 | struct in_band *in_band = in_band_; | |
064af421 | 164 | |
4701460a | 165 | if (!eth_addr_is_zero(in_band->local_mac)) { |
064af421 | 166 | status_reply_put(sr, "local-mac="ETH_ADDR_FMT, |
4701460a | 167 | ETH_ADDR_ARGS(in_band->local_mac)); |
064af421 BP |
168 | } |
169 | ||
0ad9b732 JP |
170 | if (!eth_addr_is_zero(in_band->remote_mac)) { |
171 | status_reply_put(sr, "remote-mac="ETH_ADDR_FMT, | |
172 | ETH_ADDR_ARGS(in_band->remote_mac)); | |
064af421 BP |
173 | } |
174 | } | |
175 | ||
176 | static void | |
177 | drop_flow(struct in_band *in_band, int rule_idx) | |
178 | { | |
179 | struct ib_rule *rule = &in_band->rules[rule_idx]; | |
180 | ||
181 | if (rule->installed) { | |
182 | rule->installed = false; | |
183 | ofproto_delete_flow(in_band->ofproto, &rule->flow, rule->wildcards, | |
184 | rule->priority); | |
185 | } | |
186 | } | |
187 | ||
188 | /* out_port and fixed_fields are assumed never to change. */ | |
189 | static void | |
190 | setup_flow(struct in_band *in_band, int rule_idx, const flow_t *flow, | |
191 | uint32_t fixed_fields, uint16_t out_port) | |
192 | { | |
193 | struct ib_rule *rule = &in_band->rules[rule_idx]; | |
194 | ||
195 | if (!rule->installed || memcmp(flow, &rule->flow, sizeof *flow)) { | |
196 | union ofp_action action; | |
197 | ||
198 | drop_flow(in_band, rule_idx); | |
199 | ||
200 | rule->installed = true; | |
201 | rule->flow = *flow; | |
202 | rule->wildcards = OFPFW_ALL & ~fixed_fields; | |
203 | rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx); | |
204 | ||
205 | action.type = htons(OFPAT_OUTPUT); | |
206 | action.output.len = htons(sizeof action); | |
207 | action.output.port = htons(out_port); | |
208 | action.output.max_len = htons(0); | |
209 | ofproto_add_flow(in_band->ofproto, &rule->flow, rule->wildcards, | |
210 | rule->priority, &action, 1, 0); | |
211 | } | |
212 | } | |
213 | ||
0ad9b732 JP |
214 | /* Returns true if 'packet' should be sent to the local port regardless |
215 | * of the flow table. */ | |
216 | bool | |
217 | in_band_msg_in_hook(struct in_band *in_band, const flow_t *flow, | |
218 | const struct ofpbuf *packet) | |
219 | { | |
220 | if (!in_band) { | |
221 | return false; | |
222 | } | |
223 | ||
224 | /* Regardless of how the flow table is configured, we want to be | |
225 | * able to see replies to our DHCP requests. */ | |
226 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
227 | && flow->nw_proto == IP_TYPE_UDP | |
228 | && flow->tp_src == htons(DHCP_SERVER_PORT) | |
229 | && flow->tp_dst == htons(DHCP_CLIENT_PORT) | |
230 | && packet->l7) { | |
231 | struct dhcp_header *dhcp; | |
232 | const uint8_t *local_mac; | |
233 | ||
234 | dhcp = ofpbuf_at(packet, (char *)packet->l7 - (char *)packet->data, | |
235 | sizeof *dhcp); | |
236 | if (!dhcp) { | |
237 | return false; | |
238 | } | |
239 | ||
240 | local_mac = get_local_mac(in_band); | |
241 | if (eth_addr_equals(dhcp->chaddr, local_mac)) { | |
242 | return true; | |
243 | } | |
244 | } | |
245 | ||
246 | return false; | |
247 | } | |
248 | ||
249 | /* Returns true if the rule that would match 'flow' with 'actions' is | |
250 | * allowed to be set up in the datapath. */ | |
251 | bool | |
252 | in_band_rule_check(struct in_band *in_band, const flow_t *flow, | |
253 | const struct odp_actions *actions) | |
254 | { | |
255 | if (!in_band) { | |
256 | return true; | |
257 | } | |
258 | ||
259 | /* Don't allow flows that would prevent DHCP replies from being seen | |
260 | * by the local port. */ | |
261 | if (flow->dl_type == htons(ETH_TYPE_IP) | |
262 | && flow->nw_proto == IP_TYPE_UDP | |
263 | && flow->tp_src == htons(DHCP_SERVER_PORT) | |
264 | && flow->tp_dst == htons(DHCP_CLIENT_PORT)) { | |
265 | int i; | |
266 | ||
267 | for (i=0; i<actions->n_actions; i++) { | |
268 | if (actions->actions[i].output.type == ODPAT_OUTPUT | |
269 | && actions->actions[i].output.port == ODPP_LOCAL) { | |
270 | return true; | |
271 | } | |
272 | } | |
273 | return false; | |
274 | } | |
275 | ||
276 | return true; | |
277 | } | |
278 | ||
064af421 BP |
279 | void |
280 | in_band_run(struct in_band *in_band) | |
281 | { | |
0ad9b732 JP |
282 | time_t now = time_now(); |
283 | uint32_t controller_ip; | |
284 | const uint8_t *remote_mac; | |
064af421 BP |
285 | const uint8_t *local_mac; |
286 | flow_t flow; | |
287 | ||
0ad9b732 JP |
288 | if (now < in_band->next_remote_refresh |
289 | && now < in_band->next_local_refresh) { | |
064af421 BP |
290 | return; |
291 | } | |
064af421 | 292 | |
0ad9b732 JP |
293 | controller_ip = rconn_get_remote_ip(in_band->controller); |
294 | if (in_band->controller_ip && controller_ip != in_band->controller_ip) { | |
295 | VLOG_DBG("controller IP address changed from "IP_FMT" to "IP_FMT, | |
296 | IP_ARGS(&in_band->controller_ip), | |
297 | IP_ARGS(&controller_ip)); | |
298 | } | |
299 | in_band->controller_ip = controller_ip; | |
300 | ||
301 | remote_mac = get_remote_mac(in_band); | |
302 | local_mac = get_local_mac(in_band); | |
064af421 | 303 | |
064af421 | 304 | if (local_mac) { |
0ad9b732 | 305 | /* Allow DHCP requests to be sent from the local port. */ |
064af421 | 306 | memset(&flow, 0, sizeof flow); |
0ad9b732 | 307 | flow.in_port = ODPP_LOCAL; |
a5f37a2d | 308 | flow.dl_type = htons(ETH_TYPE_IP); |
0ad9b732 JP |
309 | memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN); |
310 | flow.nw_proto = IP_TYPE_UDP; | |
311 | flow.tp_src = htons(DHCP_CLIENT_PORT); | |
312 | flow.tp_dst = htons(DHCP_SERVER_PORT); | |
313 | setup_flow(in_band, IBR_FROM_LOCAL_DHCP, &flow, | |
314 | (OFPFW_IN_PORT | OFPFW_DL_TYPE | OFPFW_DL_SRC | |
315 | | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST), | |
316 | OFPP_NORMAL); | |
317 | ||
318 | /* Allow the connection's interface to receive directed ARP traffic. */ | |
319 | memset(&flow, 0, sizeof flow); | |
320 | flow.dl_type = htons(ETH_TYPE_ARP); | |
064af421 | 321 | memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN); |
0ad9b732 JP |
322 | flow.nw_proto = ARP_OP_REPLY; |
323 | setup_flow(in_band, IBR_TO_LOCAL_ARP, &flow, | |
324 | (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), | |
325 | OFPP_NORMAL); | |
26d9fe3b JP |
326 | |
327 | /* Allow the connection's interface to be the source of ARP traffic. */ | |
328 | memset(&flow, 0, sizeof flow); | |
329 | flow.dl_type = htons(ETH_TYPE_ARP); | |
330 | memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN); | |
0ad9b732 JP |
331 | flow.nw_proto = ARP_OP_REQUEST; |
332 | setup_flow(in_band, IBR_FROM_LOCAL_ARP, &flow, | |
333 | (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), | |
334 | OFPP_NORMAL); | |
335 | } else { | |
336 | drop_flow(in_band, IBR_TO_LOCAL_ARP); | |
337 | drop_flow(in_band, IBR_FROM_LOCAL_ARP); | |
338 | } | |
a5f37a2d | 339 | |
0ad9b732 JP |
340 | if (remote_mac) { |
341 | /* Allow ARP replies to the remote side's MAC. */ | |
a5f37a2d JP |
342 | memset(&flow, 0, sizeof flow); |
343 | flow.dl_type = htons(ETH_TYPE_ARP); | |
0ad9b732 JP |
344 | memcpy(flow.dl_dst, remote_mac, ETH_ADDR_LEN); |
345 | flow.nw_proto = ARP_OP_REPLY; | |
346 | setup_flow(in_band, IBR_TO_REMOTE_ARP, &flow, | |
347 | (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), | |
348 | OFPP_NORMAL); | |
349 | ||
350 | /* Allow ARP requests from the remote side's MAC. */ | |
351 | memset(&flow, 0, sizeof flow); | |
352 | flow.dl_type = htons(ETH_TYPE_ARP); | |
353 | memcpy(flow.dl_src, remote_mac, ETH_ADDR_LEN); | |
354 | flow.nw_proto = ARP_OP_REQUEST; | |
355 | setup_flow(in_band, IBR_FROM_REMOTE_ARP, &flow, | |
356 | (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), | |
357 | OFPP_NORMAL); | |
064af421 | 358 | } else { |
0ad9b732 JP |
359 | drop_flow(in_band, IBR_TO_REMOTE_ARP); |
360 | drop_flow(in_band, IBR_FROM_REMOTE_ARP); | |
064af421 BP |
361 | } |
362 | ||
0ad9b732 JP |
363 | if (controller_ip) { |
364 | /* Allow ARP replies to the controller's IP. */ | |
064af421 BP |
365 | memset(&flow, 0, sizeof flow); |
366 | flow.dl_type = htons(ETH_TYPE_ARP); | |
0ad9b732 JP |
367 | flow.nw_proto = ARP_OP_REPLY; |
368 | flow.nw_dst = controller_ip; | |
369 | setup_flow(in_band, IBR_TO_CTL_ARP, &flow, | |
370 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK), | |
064af421 BP |
371 | OFPP_NORMAL); |
372 | ||
0ad9b732 JP |
373 | /* Allow ARP requests from the controller's IP. */ |
374 | memset(&flow, 0, sizeof flow); | |
375 | flow.dl_type = htons(ETH_TYPE_ARP); | |
376 | flow.nw_proto = ARP_OP_REQUEST; | |
377 | flow.nw_src = controller_ip; | |
378 | setup_flow(in_band, IBR_FROM_CTL_ARP, &flow, | |
379 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK), | |
380 | OFPP_NORMAL); | |
381 | ||
064af421 BP |
382 | /* OpenFlow traffic to or from the controller. |
383 | * | |
384 | * (A given field's value is completely ignored if it is wildcarded, | |
385 | * which is why we can get away with using a single 'flow' in each | |
386 | * case here.) */ | |
387 | memset(&flow, 0, sizeof flow); | |
388 | flow.dl_type = htons(ETH_TYPE_IP); | |
064af421 | 389 | flow.nw_proto = IP_TYPE_TCP; |
0ad9b732 JP |
390 | flow.nw_src = controller_ip; |
391 | flow.nw_dst = controller_ip; | |
064af421 BP |
392 | flow.tp_src = htons(OFP_TCP_PORT); |
393 | flow.tp_dst = htons(OFP_TCP_PORT); | |
0ad9b732 JP |
394 | setup_flow(in_band, IBR_TO_CTL_OFP, &flow, |
395 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK | |
064af421 | 396 | | OFPFW_TP_DST), OFPP_NORMAL); |
0ad9b732 JP |
397 | setup_flow(in_band, IBR_FROM_CTL_OFP, &flow, |
398 | (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | |
064af421 | 399 | | OFPFW_TP_SRC), OFPP_NORMAL); |
064af421 | 400 | } else { |
0ad9b732 JP |
401 | drop_flow(in_band, IBR_TO_CTL_ARP); |
402 | drop_flow(in_band, IBR_FROM_CTL_ARP); | |
403 | drop_flow(in_band, IBR_TO_CTL_OFP); | |
404 | drop_flow(in_band, IBR_FROM_CTL_OFP); | |
064af421 BP |
405 | } |
406 | } | |
407 | ||
408 | void | |
409 | in_band_wait(struct in_band *in_band) | |
410 | { | |
411 | time_t now = time_now(); | |
0ad9b732 JP |
412 | time_t wakeup |
413 | = MIN(in_band->next_remote_refresh, in_band->next_local_refresh); | |
064af421 BP |
414 | if (wakeup > now) { |
415 | poll_timer_wait((wakeup - now) * 1000); | |
416 | } else { | |
417 | poll_immediate_wake(); | |
418 | } | |
419 | } | |
420 | ||
421 | void | |
422 | in_band_flushed(struct in_band *in_band) | |
423 | { | |
424 | int i; | |
425 | ||
426 | for (i = 0; i < N_IB_RULES; i++) { | |
427 | in_band->rules[i].installed = false; | |
428 | } | |
429 | } | |
430 | ||
26d9fe3b | 431 | void |
0ad9b732 JP |
432 | in_band_create(struct ofproto *ofproto, struct dpif *dpif, |
433 | struct switch_status *ss, struct rconn *controller, | |
434 | struct in_band **in_bandp) | |
064af421 BP |
435 | { |
436 | struct in_band *in_band; | |
0ad9b732 | 437 | int error; |
064af421 BP |
438 | |
439 | in_band = xcalloc(1, sizeof *in_band); | |
0ad9b732 JP |
440 | error = dpif_port_get_name(dpif, ODPP_LOCAL, in_band->local_name, |
441 | sizeof in_band->local_name); | |
442 | if (error) { | |
443 | free(in_band); | |
444 | return; | |
445 | } | |
446 | ||
064af421 | 447 | in_band->ofproto = ofproto; |
064af421 BP |
448 | in_band->controller = controller; |
449 | in_band->ss_cat = switch_status_register(ss, "in-band", | |
450 | in_band_status_cb, in_band); | |
0ad9b732 | 451 | in_band->next_remote_refresh = TIME_MIN; |
064af421 BP |
452 | in_band->next_local_refresh = TIME_MIN; |
453 | ||
454 | *in_bandp = in_band; | |
064af421 BP |
455 | } |
456 | ||
457 | void | |
458 | in_band_destroy(struct in_band *in_band) | |
459 | { | |
460 | if (in_band) { | |
064af421 BP |
461 | switch_status_unregister(in_band->ss_cat); |
462 | /* We don't own the rconn. */ | |
463 | } | |
464 | } | |
465 |