]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
3fbbcba7 | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. |
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> | |
b2befd5b BP |
18 | #include <sys/types.h> |
19 | #include <netinet/in.h> | |
064af421 BP |
20 | #include <arpa/inet.h> |
21 | #include <errno.h> | |
22 | #include <inttypes.h> | |
9d82ec47 | 23 | #include <sys/socket.h> |
064af421 BP |
24 | #include <net/if.h> |
25 | #include <string.h> | |
26d9fe3b | 26 | #include <stdlib.h> |
cf3fad8a | 27 | #include "classifier.h" |
0ad9b732 | 28 | #include "dhcp.h" |
064af421 | 29 | #include "flow.h" |
b598f214 | 30 | #include "in-band.h" |
064af421 | 31 | #include "netdev.h" |
cdee00fd | 32 | #include "netlink.h" |
064af421 | 33 | #include "odp-util.h" |
064af421 | 34 | #include "ofproto.h" |
5bee6e26 | 35 | #include "ofproto-provider.h" |
064af421 | 36 | #include "openflow/openflow.h" |
b598f214 BW |
37 | #include "openvswitch/ofp-actions.h" |
38 | #include "openvswitch/ofpbuf.h" | |
39 | #include "openvswitch/vlog.h" | |
064af421 | 40 | #include "packets.h" |
fd016ae3 | 41 | #include "openvswitch/poll-loop.h" |
064af421 | 42 | #include "timeval.h" |
064af421 | 43 | |
d98e6007 | 44 | VLOG_DEFINE_THIS_MODULE(in_band); |
5136ce49 | 45 | |
0ade584e BP |
46 | /* Priorities used in classifier for in-band rules. These values are higher |
47 | * than any that may be set with OpenFlow, and "18" kind of looks like "IB". | |
48 | * The ordering of priorities is not important because all of the rules set up | |
49 | * by in-band control have the same action. The only reason to use more than | |
50 | * one priority is to make the kind of flow easier to see during debugging. */ | |
064af421 | 51 | enum { |
d2ede7bc | 52 | /* One set per bridge. */ |
0ade584e | 53 | IBR_FROM_LOCAL_DHCP = 180000, /* (a) From local port, DHCP. */ |
85088747 JP |
54 | IBR_TO_LOCAL_ARP, /* (b) To local port, ARP. */ |
55 | IBR_FROM_LOCAL_ARP, /* (c) From local port, ARP. */ | |
d2ede7bc BP |
56 | |
57 | /* One set per unique next-hop MAC. */ | |
58 | IBR_TO_NEXT_HOP_ARP, /* (d) To remote MAC, ARP. */ | |
59 | IBR_FROM_NEXT_HOP_ARP, /* (e) From remote MAC, ARP. */ | |
60 | ||
61 | /* One set per unique remote IP address. */ | |
62 | IBR_TO_REMOTE_ARP, /* (f) To remote IP, ARP. */ | |
63 | IBR_FROM_REMOTE_ARP, /* (g) From remote IP, ARP. */ | |
64 | ||
65 | /* One set per unique remote (IP,port) pair. */ | |
66 | IBR_TO_REMOTE_TCP, /* (h) To remote IP, TCP port. */ | |
67 | IBR_FROM_REMOTE_TCP /* (i) From remote IP, TCP port. */ | |
064af421 BP |
68 | }; |
69 | ||
0ade584e BP |
70 | /* Track one remote IP and next hop information. */ |
71 | struct in_band_remote { | |
74ff3298 JR |
72 | struct sockaddr_in remote_addr; /* IP address, in network byte order. */ |
73 | struct eth_addr remote_mac; /* Next-hop MAC, all-zeros if unknown. */ | |
74 | struct eth_addr last_remote_mac; /* Previous nonzero next-hop MAC. */ | |
75 | struct netdev *remote_netdev; /* Device to send to next-hop MAC. */ | |
0ade584e BP |
76 | }; |
77 | ||
7ee20df1 BP |
78 | /* What to do to an in_band_rule. */ |
79 | enum in_band_op { | |
80 | ADD, /* Add the rule to ofproto's flow table. */ | |
f72e3073 | 81 | DEL /* Delete the rule from ofproto's flow table. */ |
7ee20df1 BP |
82 | }; |
83 | ||
84 | /* A rule to add to or delete from ofproto's flow table. */ | |
85 | struct in_band_rule { | |
81a76618 BP |
86 | struct hmap_node hmap_node; /* In struct in_band's "rules" hmap. */ |
87 | struct match match; | |
eb391b76 | 88 | int priority; |
7ee20df1 BP |
89 | enum in_band_op op; |
90 | }; | |
91 | ||
064af421 BP |
92 | struct in_band { |
93 | struct ofproto *ofproto; | |
7ee20df1 | 94 | int queue_id; |
064af421 | 95 | |
0ade584e BP |
96 | /* Remote information. */ |
97 | time_t next_remote_refresh; /* Refresh timer. */ | |
98 | struct in_band_remote *remotes; | |
99 | size_t n_remotes; | |
100 | ||
101 | /* Local information. */ | |
102 | time_t next_local_refresh; /* Refresh timer. */ | |
74ff3298 | 103 | struct eth_addr local_mac; /* Current MAC. */ |
0ade584e BP |
104 | struct netdev *local_netdev; /* Local port's network device. */ |
105 | ||
7ee20df1 BP |
106 | /* Flow tracking. */ |
107 | struct hmap rules; /* Contains "struct in_band_rule"s. */ | |
064af421 BP |
108 | }; |
109 | ||
110 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); | |
111 | ||
0ade584e BP |
112 | static int |
113 | refresh_remote(struct in_band *ib, struct in_band_remote *r) | |
064af421 | 114 | { |
0ade584e | 115 | struct in_addr next_hop_inaddr; |
f1acd62b | 116 | char *next_hop_dev; |
0ade584e | 117 | int retval; |
064af421 | 118 | |
0ade584e | 119 | /* Find the next-hop IP address. */ |
74ff3298 | 120 | r->remote_mac = eth_addr_zero; |
d2ede7bc | 121 | retval = netdev_get_next_hop(ib->local_netdev, &r->remote_addr.sin_addr, |
0ade584e BP |
122 | &next_hop_inaddr, &next_hop_dev); |
123 | if (retval) { | |
0ca7bf8a | 124 | VLOG_WARN_RL(&rl, "%s: cannot find route for controller " |
125 | "("IP_FMT"): %s", | |
126 | ib->ofproto->name, | |
127 | IP_ARGS(r->remote_addr.sin_addr.s_addr), | |
128 | ovs_strerror(retval)); | |
0ade584e BP |
129 | return 1; |
130 | } | |
131 | if (!next_hop_inaddr.s_addr) { | |
d2ede7bc | 132 | next_hop_inaddr = r->remote_addr.sin_addr; |
0ade584e | 133 | } |
c752217a | 134 | |
d2ede7bc | 135 | /* Open the next-hop network device. */ |
0ade584e BP |
136 | if (!r->remote_netdev |
137 | || strcmp(netdev_get_name(r->remote_netdev), next_hop_dev)) | |
138 | { | |
139 | netdev_close(r->remote_netdev); | |
064af421 | 140 | |
40baa1f5 | 141 | retval = netdev_open(next_hop_dev, NULL, &r->remote_netdev); |
0ad9b732 | 142 | if (retval) { |
4e311c99 | 143 | VLOG_WARN_RL(&rl, "%s: cannot open netdev %s (next hop " |
0ade584e | 144 | "to controller "IP_FMT"): %s", |
4e311c99 AW |
145 | ib->ofproto->name, next_hop_dev, |
146 | IP_ARGS(r->remote_addr.sin_addr.s_addr), | |
10a89ef0 | 147 | ovs_strerror(retval)); |
0ade584e BP |
148 | free(next_hop_dev); |
149 | return 1; | |
064af421 | 150 | } |
0ade584e BP |
151 | } |
152 | free(next_hop_dev); | |
153 | ||
154 | /* Look up the MAC address of the next-hop IP address. */ | |
155 | retval = netdev_arp_lookup(r->remote_netdev, next_hop_inaddr.s_addr, | |
74ff3298 | 156 | &r->remote_mac); |
0ade584e | 157 | if (retval) { |
4e311c99 AW |
158 | VLOG_DBG_RL(&rl, "%s: cannot look up remote MAC address ("IP_FMT"): %s", |
159 | ib->ofproto->name, IP_ARGS(next_hop_inaddr.s_addr), | |
160 | ovs_strerror(retval)); | |
064af421 | 161 | } |
0ad9b732 | 162 | |
6dee2066 BP |
163 | /* If we don't have a MAC address, then refresh quickly, since we probably |
164 | * will get a MAC address soon (via ARP). Otherwise, we can afford to wait | |
165 | * a little while. */ | |
166 | return eth_addr_is_zero(r->remote_mac) ? 1 : 10; | |
064af421 BP |
167 | } |
168 | ||
0ade584e BP |
169 | static bool |
170 | refresh_remotes(struct in_band *ib) | |
064af421 | 171 | { |
0ade584e BP |
172 | struct in_band_remote *r; |
173 | bool any_changes; | |
0ade584e BP |
174 | |
175 | if (time_now() < ib->next_remote_refresh) { | |
176 | return false; | |
177 | } | |
178 | ||
179 | any_changes = false; | |
5dbdfff7 | 180 | ib->next_remote_refresh = TIME_MAX; |
0ade584e | 181 | for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) { |
74ff3298 | 182 | struct eth_addr old_remote_mac; |
5dbdfff7 | 183 | time_t next_refresh; |
0ade584e | 184 | |
6dee2066 | 185 | /* Save old MAC. */ |
74ff3298 | 186 | old_remote_mac = r->remote_mac; |
0ade584e BP |
187 | |
188 | /* Refresh remote information. */ | |
5dbdfff7 BP |
189 | next_refresh = refresh_remote(ib, r) + time_now(); |
190 | ib->next_remote_refresh = MIN(ib->next_remote_refresh, next_refresh); | |
0ade584e | 191 | |
6dee2066 | 192 | /* If the MAC changed, log the changes. */ |
0ade584e BP |
193 | if (!eth_addr_equals(r->remote_mac, old_remote_mac)) { |
194 | any_changes = true; | |
195 | if (!eth_addr_is_zero(r->remote_mac) | |
196 | && !eth_addr_equals(r->last_remote_mac, r->remote_mac)) { | |
4e311c99 | 197 | VLOG_DBG("%s: remote MAC address changed from "ETH_ADDR_FMT |
0ade584e | 198 | " to "ETH_ADDR_FMT, |
4e311c99 | 199 | ib->ofproto->name, |
0ade584e BP |
200 | ETH_ADDR_ARGS(r->last_remote_mac), |
201 | ETH_ADDR_ARGS(r->remote_mac)); | |
74ff3298 | 202 | r->last_remote_mac = r->remote_mac; |
0ade584e | 203 | } |
064af421 | 204 | } |
064af421 | 205 | } |
0ade584e BP |
206 | |
207 | return any_changes; | |
064af421 BP |
208 | } |
209 | ||
0ade584e BP |
210 | /* Refreshes the MAC address of the local port into ib->local_mac, if it is due |
211 | * for a refresh. Returns true if anything changed, otherwise false. */ | |
212 | static bool | |
213 | refresh_local(struct in_band *ib) | |
064af421 | 214 | { |
74ff3298 | 215 | struct eth_addr ea; |
0ade584e | 216 | time_t now; |
064af421 | 217 | |
0ade584e BP |
218 | now = time_now(); |
219 | if (now < ib->next_local_refresh) { | |
220 | return false; | |
064af421 | 221 | } |
0ade584e | 222 | ib->next_local_refresh = now + 1; |
064af421 | 223 | |
74ff3298 | 224 | if (netdev_get_etheraddr(ib->local_netdev, &ea) |
0ade584e BP |
225 | || eth_addr_equals(ea, ib->local_mac)) { |
226 | return false; | |
064af421 | 227 | } |
064af421 | 228 | |
74ff3298 | 229 | ib->local_mac = ea; |
0ade584e | 230 | return true; |
064af421 BP |
231 | } |
232 | ||
ce4a6b76 BP |
233 | /* Returns true if packets in 'flow' should be directed to the local port. |
234 | * (This keeps the flow table from preventing DHCP replies from being seen by | |
235 | * the local port.) */ | |
0ad9b732 | 236 | bool |
ce4a6b76 | 237 | in_band_must_output_to_local_port(const struct flow *flow) |
0ad9b732 | 238 | { |
ce4a6b76 | 239 | return (flow->dl_type == htons(ETH_TYPE_IP) |
6767a2cc | 240 | && flow->nw_proto == IPPROTO_UDP |
d295e8e9 | 241 | && flow->tp_src == htons(DHCP_SERVER_PORT) |
ce4a6b76 | 242 | && flow->tp_dst == htons(DHCP_CLIENT_PORT)); |
0ad9b732 JP |
243 | } |
244 | ||
3fbbcba7 BP |
245 | /* Returns the number of in-band rules currently installed in the flow |
246 | * table. */ | |
247 | int | |
248 | in_band_count_rules(const struct in_band *ib) | |
249 | { | |
250 | return hmap_count(&ib->rules); | |
251 | } | |
252 | ||
0ade584e | 253 | static void |
eb391b76 | 254 | add_rule(struct in_band *ib, const struct match *match, int priority) |
0ade584e | 255 | { |
81a76618 | 256 | uint32_t hash = match_hash(match, 0); |
7ee20df1 BP |
257 | struct in_band_rule *rule; |
258 | ||
81a76618 BP |
259 | HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &ib->rules) { |
260 | if (match_equal(&rule->match, match)) { | |
7ee20df1 BP |
261 | rule->op = ADD; |
262 | return; | |
263 | } | |
264 | } | |
265 | ||
266 | rule = xmalloc(sizeof *rule); | |
81a76618 BP |
267 | rule->match = *match; |
268 | rule->priority = priority; | |
7ee20df1 | 269 | rule->op = ADD; |
81a76618 | 270 | hmap_insert(&ib->rules, &rule->hmap_node, hash); |
7ee20df1 BP |
271 | } |
272 | ||
273 | static void | |
274 | update_rules(struct in_band *ib) | |
275 | { | |
276 | struct in_band_rule *ib_rule; | |
277 | struct in_band_remote *r; | |
81a76618 | 278 | struct match match; |
0ade584e | 279 | |
7ee20df1 BP |
280 | /* Mark all the existing rules for deletion. (Afterward we will re-add any |
281 | * rules that are still valid.) */ | |
81a76618 | 282 | HMAP_FOR_EACH (ib_rule, hmap_node, &ib->rules) { |
f72e3073 | 283 | ib_rule->op = DEL; |
7ee20df1 BP |
284 | } |
285 | ||
6da1e809 | 286 | if (ib->n_remotes && !eth_addr_is_zero(ib->local_mac)) { |
d2ede7bc | 287 | /* (a) Allow DHCP requests sent from the local port. */ |
81a76618 BP |
288 | match_init_catchall(&match); |
289 | match_set_in_port(&match, OFPP_LOCAL); | |
290 | match_set_dl_type(&match, htons(ETH_TYPE_IP)); | |
291 | match_set_dl_src(&match, ib->local_mac); | |
292 | match_set_nw_proto(&match, IPPROTO_UDP); | |
293 | match_set_tp_src(&match, htons(DHCP_CLIENT_PORT)); | |
294 | match_set_tp_dst(&match, htons(DHCP_SERVER_PORT)); | |
295 | add_rule(ib, &match, IBR_FROM_LOCAL_DHCP); | |
0ad9b732 | 296 | |
d2ede7bc | 297 | /* (b) Allow ARP replies to the local port's MAC address. */ |
81a76618 BP |
298 | match_init_catchall(&match); |
299 | match_set_dl_type(&match, htons(ETH_TYPE_ARP)); | |
300 | match_set_dl_dst(&match, ib->local_mac); | |
301 | match_set_nw_proto(&match, ARP_OP_REPLY); | |
302 | add_rule(ib, &match, IBR_TO_LOCAL_ARP); | |
26d9fe3b | 303 | |
d2ede7bc | 304 | /* (c) Allow ARP requests from the local port's MAC address. */ |
81a76618 BP |
305 | match_init_catchall(&match); |
306 | match_set_dl_type(&match, htons(ETH_TYPE_ARP)); | |
307 | match_set_dl_src(&match, ib->local_mac); | |
308 | match_set_nw_proto(&match, ARP_OP_REQUEST); | |
309 | add_rule(ib, &match, IBR_FROM_LOCAL_ARP); | |
0ad9b732 | 310 | } |
a5f37a2d | 311 | |
7ee20df1 | 312 | for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) { |
74ff3298 | 313 | if (eth_addr_is_zero(r->remote_mac)) { |
7ee20df1 | 314 | continue; |
0ade584e BP |
315 | } |
316 | ||
d2ede7bc | 317 | /* (d) Allow ARP replies to the next hop's MAC address. */ |
81a76618 BP |
318 | match_init_catchall(&match); |
319 | match_set_dl_type(&match, htons(ETH_TYPE_ARP)); | |
74ff3298 | 320 | match_set_dl_dst(&match, r->remote_mac); |
81a76618 BP |
321 | match_set_nw_proto(&match, ARP_OP_REPLY); |
322 | add_rule(ib, &match, IBR_TO_NEXT_HOP_ARP); | |
0ade584e | 323 | |
d2ede7bc | 324 | /* (e) Allow ARP requests from the next hop's MAC address. */ |
81a76618 BP |
325 | match_init_catchall(&match); |
326 | match_set_dl_type(&match, htons(ETH_TYPE_ARP)); | |
74ff3298 | 327 | match_set_dl_src(&match, r->remote_mac); |
81a76618 BP |
328 | match_set_nw_proto(&match, ARP_OP_REQUEST); |
329 | add_rule(ib, &match, IBR_FROM_NEXT_HOP_ARP); | |
064af421 BP |
330 | } |
331 | ||
7ee20df1 BP |
332 | for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) { |
333 | const struct sockaddr_in *a = &r->remote_addr; | |
0ade584e | 334 | |
7ee20df1 BP |
335 | /* (f) Allow ARP replies containing the remote's IP address as a |
336 | * target. */ | |
81a76618 BP |
337 | match_init_catchall(&match); |
338 | match_set_dl_type(&match, htons(ETH_TYPE_ARP)); | |
339 | match_set_nw_proto(&match, ARP_OP_REPLY); | |
340 | match_set_nw_dst(&match, a->sin_addr.s_addr); | |
341 | add_rule(ib, &match, IBR_TO_REMOTE_ARP); | |
c16e55cf | 342 | |
7ee20df1 BP |
343 | /* (g) Allow ARP requests containing the remote's IP address as a |
344 | * source. */ | |
81a76618 BP |
345 | match_init_catchall(&match); |
346 | match_set_dl_type(&match, htons(ETH_TYPE_ARP)); | |
347 | match_set_nw_proto(&match, ARP_OP_REQUEST); | |
348 | match_set_nw_src(&match, a->sin_addr.s_addr); | |
349 | add_rule(ib, &match, IBR_FROM_REMOTE_ARP); | |
c16e55cf | 350 | |
7ee20df1 | 351 | /* (h) Allow TCP traffic to the remote's IP and port. */ |
81a76618 BP |
352 | match_init_catchall(&match); |
353 | match_set_dl_type(&match, htons(ETH_TYPE_IP)); | |
354 | match_set_nw_proto(&match, IPPROTO_TCP); | |
355 | match_set_nw_dst(&match, a->sin_addr.s_addr); | |
356 | match_set_tp_dst(&match, a->sin_port); | |
357 | add_rule(ib, &match, IBR_TO_REMOTE_TCP); | |
c16e55cf | 358 | |
7ee20df1 | 359 | /* (i) Allow TCP traffic from the remote's IP and port. */ |
81a76618 BP |
360 | match_init_catchall(&match); |
361 | match_set_dl_type(&match, htons(ETH_TYPE_IP)); | |
362 | match_set_nw_proto(&match, IPPROTO_TCP); | |
363 | match_set_nw_src(&match, a->sin_addr.s_addr); | |
364 | match_set_tp_src(&match, a->sin_port); | |
365 | add_rule(ib, &match, IBR_FROM_REMOTE_TCP); | |
7ee20df1 | 366 | } |
0ade584e BP |
367 | } |
368 | ||
6da1e809 BP |
369 | /* Updates the OpenFlow flow table for the current state of in-band control. |
370 | * Returns true ordinarily. Returns false if no remotes are configured on 'ib' | |
371 | * and 'ib' doesn't have any rules left to remove from the OpenFlow flow | |
372 | * table. Thus, a false return value means that the caller can destroy 'ib' | |
373 | * without leaving extra flows hanging around in the flow table. */ | |
374 | bool | |
7ee20df1 | 375 | in_band_run(struct in_band *ib) |
0ade584e | 376 | { |
f25d0cf3 BP |
377 | uint64_t ofpacts_stub[128 / 8]; |
378 | struct ofpbuf ofpacts; | |
b1da6250 | 379 | |
7ee20df1 | 380 | struct in_band_rule *rule, *next; |
b1da6250 | 381 | |
f25d0cf3 BP |
382 | ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); |
383 | ||
384 | if (ib->queue_id >= 0) { | |
385 | ofpact_put_SET_QUEUE(&ofpacts)->queue_id = ib->queue_id; | |
d2ede7bc | 386 | } |
f25d0cf3 | 387 | ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; |
0ade584e | 388 | |
7ee20df1 BP |
389 | refresh_local(ib); |
390 | refresh_remotes(ib); | |
0ade584e | 391 | |
7ee20df1 | 392 | update_rules(ib); |
0ade584e | 393 | |
81a76618 | 394 | HMAP_FOR_EACH_SAFE (rule, next, hmap_node, &ib->rules) { |
7ee20df1 BP |
395 | switch (rule->op) { |
396 | case ADD: | |
81a76618 | 397 | ofproto_add_flow(ib->ofproto, &rule->match, rule->priority, |
6fd6ed71 | 398 | ofpacts.data, ofpacts.size); |
7ee20df1 | 399 | break; |
0ade584e | 400 | |
f72e3073 | 401 | case DEL: |
25010c68 | 402 | ovs_mutex_lock(&ofproto_mutex); |
b20f4073 | 403 | ofproto_delete_flow(ib->ofproto, &rule->match, rule->priority); |
25010c68 | 404 | ovs_mutex_unlock(&ofproto_mutex); |
b20f4073 BP |
405 | hmap_remove(&ib->rules, &rule->hmap_node); |
406 | free(rule); | |
7ee20df1 | 407 | break; |
0ade584e BP |
408 | } |
409 | } | |
6da1e809 | 410 | |
f25d0cf3 BP |
411 | ofpbuf_uninit(&ofpacts); |
412 | ||
6da1e809 | 413 | return ib->n_remotes || !hmap_is_empty(&ib->rules); |
0ade584e BP |
414 | } |
415 | ||
064af421 BP |
416 | void |
417 | in_band_wait(struct in_band *in_band) | |
418 | { | |
7cf8b266 | 419 | long long int wakeup |
0ad9b732 | 420 | = MIN(in_band->next_remote_refresh, in_band->next_local_refresh); |
7cf8b266 | 421 | poll_timer_wait_until(wakeup * 1000); |
064af421 BP |
422 | } |
423 | ||
f1acd62b | 424 | int |
19a87e36 | 425 | in_band_create(struct ofproto *ofproto, const char *local_name, |
9b45d7f5 | 426 | struct in_band **in_bandp) |
064af421 BP |
427 | { |
428 | struct in_band *in_band; | |
f1acd62b | 429 | struct netdev *local_netdev; |
0ad9b732 | 430 | int error; |
c381bca5 | 431 | const char *type = ofproto_port_open_type(ofproto, "internal"); |
064af421 | 432 | |
928ef386 | 433 | *in_bandp = NULL; |
61f4f76e | 434 | error = netdev_open(local_name, type, &local_netdev); |
f1acd62b | 435 | if (error) { |
4e311c99 AW |
436 | VLOG_ERR("%s: failed to initialize in-band control: cannot open " |
437 | "datapath local port %s (%s)", ofproto->name, | |
10a89ef0 | 438 | local_name, ovs_strerror(error)); |
f1acd62b BP |
439 | return error; |
440 | } | |
064af421 | 441 | |
ec6fde61 | 442 | in_band = xzalloc(sizeof *in_band); |
064af421 | 443 | in_band->ofproto = ofproto; |
7ee20df1 | 444 | in_band->queue_id = -1; |
f1acd62b | 445 | in_band->next_remote_refresh = TIME_MIN; |
0ade584e BP |
446 | in_band->next_local_refresh = TIME_MIN; |
447 | in_band->local_netdev = local_netdev; | |
7ee20df1 | 448 | hmap_init(&in_band->rules); |
064af421 BP |
449 | |
450 | *in_bandp = in_band; | |
f1acd62b BP |
451 | |
452 | return 0; | |
064af421 BP |
453 | } |
454 | ||
455 | void | |
0ade584e | 456 | in_band_destroy(struct in_band *ib) |
064af421 | 457 | { |
0ade584e | 458 | if (ib) { |
4ec3d7c7 | 459 | struct in_band_rule *rule; |
7ee20df1 | 460 | |
4ec3d7c7 | 461 | HMAP_FOR_EACH_POP (rule, hmap_node, &ib->rules) { |
7ee20df1 BP |
462 | free(rule); |
463 | } | |
464 | hmap_destroy(&ib->rules); | |
0ade584e | 465 | in_band_set_remotes(ib, NULL, 0); |
0ade584e BP |
466 | netdev_close(ib->local_netdev); |
467 | free(ib); | |
468 | } | |
469 | } | |
f7de2cdf | 470 | |
a3c5ac70 | 471 | static bool |
d2ede7bc BP |
472 | any_addresses_changed(struct in_band *ib, |
473 | const struct sockaddr_in *addresses, size_t n) | |
a3c5ac70 BP |
474 | { |
475 | size_t i; | |
476 | ||
477 | if (n != ib->n_remotes) { | |
478 | return true; | |
479 | } | |
480 | ||
481 | for (i = 0; i < n; i++) { | |
d2ede7bc BP |
482 | const struct sockaddr_in *old = &ib->remotes[i].remote_addr; |
483 | const struct sockaddr_in *new = &addresses[i]; | |
484 | ||
485 | if (old->sin_addr.s_addr != new->sin_addr.s_addr || | |
486 | old->sin_port != new->sin_port) { | |
a3c5ac70 BP |
487 | return true; |
488 | } | |
489 | } | |
490 | ||
491 | return false; | |
492 | } | |
493 | ||
0ade584e | 494 | void |
d2ede7bc BP |
495 | in_band_set_remotes(struct in_band *ib, |
496 | const struct sockaddr_in *addresses, size_t n) | |
0ade584e BP |
497 | { |
498 | size_t i; | |
499 | ||
d2ede7bc | 500 | if (!any_addresses_changed(ib, addresses, n)) { |
0ade584e | 501 | return; |
0ade584e BP |
502 | } |
503 | ||
a3c5ac70 | 504 | /* Clear old remotes. */ |
0ade584e | 505 | for (i = 0; i < ib->n_remotes; i++) { |
0ade584e | 506 | netdev_close(ib->remotes[i].remote_netdev); |
064af421 | 507 | } |
0ade584e | 508 | free(ib->remotes); |
064af421 | 509 | |
a3c5ac70 | 510 | /* Set up new remotes. */ |
bad0c371 | 511 | ib->remotes = n ? xzalloc(n * sizeof *ib->remotes) : NULL; |
0ade584e BP |
512 | ib->n_remotes = n; |
513 | for (i = 0; i < n; i++) { | |
d2ede7bc | 514 | ib->remotes[i].remote_addr = addresses[i]; |
0ade584e | 515 | } |
a3c5ac70 BP |
516 | |
517 | /* Force refresh in next call to in_band_run(). */ | |
518 | ib->next_remote_refresh = TIME_MIN; | |
0ade584e | 519 | } |
b1da6250 BP |
520 | |
521 | /* Sets the OpenFlow queue used by flows set up by 'ib' to 'queue_id'. If | |
522 | * 'queue_id' is negative, 'ib' will not set any queue (which is also the | |
523 | * default). */ | |
524 | void | |
525 | in_band_set_queue(struct in_band *ib, int queue_id) | |
526 | { | |
527 | ib->queue_id = queue_id; | |
528 | } | |
529 |