in-band: Implement L3-based in-band control
[sliver-openvswitch.git] / secchan / in-band.c
1 /*
2  * Copyright (c) 2008, 2009 Nicira Networks.
3  *
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:
7  *
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.
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>
24 #include <stdlib.h>
25 #include "dhcp.h"
26 #include "dpif.h"
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"
35 #include "openvswitch/datapath-protocol.h"
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 {
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. */
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;
73     struct rconn *controller;
74     struct status_category *ss_cat;
75
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;
80
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;
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 *
94 get_remote_mac(struct in_band *ib)
95 {
96     int retval;
97     bool have_mac;
98     struct in_addr c_in4, r_in4;
99     char *dev_name;
100     time_t now = time_now();
101
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;
114         }
115
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));
121         }
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 "
128                      ETH_ADDR_FMT,
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);
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. */
139         ib->next_remote_refresh 
140                 = now + (!ib->controller_ip || have_mac ? 10 : 1);
141     }
142
143     return !eth_addr_is_zero(ib->remote_mac) ? ib->remote_mac : NULL;
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];
152         if (!netdev_nodev_get_etheraddr(ib->local_name, ea)) {
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_;
164
165     if (!eth_addr_is_zero(in_band->local_mac)) {
166         status_reply_put(sr, "local-mac="ETH_ADDR_FMT,
167                          ETH_ADDR_ARGS(in_band->local_mac));
168     }
169
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));
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
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
279 void
280 in_band_run(struct in_band *in_band)
281 {
282     time_t now = time_now();
283     uint32_t controller_ip;
284     const uint8_t *remote_mac;
285     const uint8_t *local_mac;
286     flow_t flow;
287
288     if (now < in_band->next_remote_refresh 
289             && now < in_band->next_local_refresh) {
290         return;
291     }
292
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);
303
304     if (local_mac) {
305         /* Allow DHCP requests to be sent from the local port. */
306         memset(&flow, 0, sizeof flow);
307         flow.in_port = ODPP_LOCAL;
308         flow.dl_type = htons(ETH_TYPE_IP);
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);
321         memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN);
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);
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);
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     }
339
340     if (remote_mac) {
341         /* Allow ARP replies to the remote side's MAC. */
342         memset(&flow, 0, sizeof flow);
343         flow.dl_type = htons(ETH_TYPE_ARP);
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);
358     } else {
359         drop_flow(in_band, IBR_TO_REMOTE_ARP);
360         drop_flow(in_band, IBR_FROM_REMOTE_ARP);
361     }
362
363     if (controller_ip) {
364         /* Allow ARP replies to the controller's IP. */
365         memset(&flow, 0, sizeof flow);
366         flow.dl_type = htons(ETH_TYPE_ARP);
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),
371                    OFPP_NORMAL);
372
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      
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);
389         flow.nw_proto = IP_TYPE_TCP;
390         flow.nw_src = controller_ip;
391         flow.nw_dst = controller_ip;
392         flow.tp_src = htons(OFP_TCP_PORT);
393         flow.tp_dst = htons(OFP_TCP_PORT);
394         setup_flow(in_band, IBR_TO_CTL_OFP, &flow,
395                    (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK 
396                     | OFPFW_TP_DST), OFPP_NORMAL);
397         setup_flow(in_band, IBR_FROM_CTL_OFP, &flow,
398                    (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK
399                     | OFPFW_TP_SRC), OFPP_NORMAL);
400     } else {
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);
405     }
406 }
407
408 void
409 in_band_wait(struct in_band *in_band)
410 {
411     time_t now = time_now();
412     time_t wakeup 
413             = MIN(in_band->next_remote_refresh, in_band->next_local_refresh);
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
431 void
432 in_band_create(struct ofproto *ofproto, struct dpif *dpif,
433                struct switch_status *ss, struct rconn *controller, 
434                struct in_band **in_bandp)
435 {
436     struct in_band *in_band;
437     int error;
438
439     in_band = xcalloc(1, sizeof *in_band);
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
447     in_band->ofproto = ofproto;
448     in_band->controller = controller;
449     in_band->ss_cat = switch_status_register(ss, "in-band",
450                                              in_band_status_cb, in_band);
451     in_band->next_remote_refresh = TIME_MIN;
452     in_band->next_local_refresh = TIME_MIN;
453
454     *in_bandp = in_band;
455 }
456
457 void
458 in_band_destroy(struct in_band *in_band)
459 {
460     if (in_band) {
461         switch_status_unregister(in_band->ss_cat);
462         /* We don't own the rconn. */
463     }
464 }
465