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