LCOV - code coverage report
Current view: top level - ofproto - fail-open.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 4 83 4.8 %
Date: 2016-09-14 01:02:56 Functions: 2 12 16.7 %
Branches: 1 36 2.8 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2016 Nicira, Inc.
       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 <inttypes.h>
      19                 :            : #include <stdlib.h>
      20                 :            : #include "classifier.h"
      21                 :            : #include "connmgr.h"
      22                 :            : #include "dp-packet.h"
      23                 :            : #include "fail-open.h"
      24                 :            : #include "flow.h"
      25                 :            : #include "mac-learning.h"
      26                 :            : #include "odp-util.h"
      27                 :            : #include "openvswitch/ofp-actions.h"
      28                 :            : #include "openvswitch/ofp-util.h"
      29                 :            : #include "openvswitch/ofpbuf.h"
      30                 :            : #include "openvswitch/vconn.h"
      31                 :            : #include "openvswitch/vlog.h"
      32                 :            : #include "ofproto.h"
      33                 :            : #include "ofproto-provider.h"
      34                 :            : #include "poll-loop.h"
      35                 :            : #include "rconn.h"
      36                 :            : #include "timeval.h"
      37                 :            : 
      38                 :       1288 : VLOG_DEFINE_THIS_MODULE(fail_open);
      39                 :            : 
      40                 :            : /*
      41                 :            :  * Fail-open mode.
      42                 :            :  *
      43                 :            :  * In fail-open mode, the switch detects when the controller cannot be
      44                 :            :  * contacted or when the controller is dropping switch connections because the
      45                 :            :  * switch does not pass its admission control policy.  In those situations the
      46                 :            :  * switch sets up flows itself using the "normal" action.
      47                 :            :  *
      48                 :            :  * There is a little subtlety to implementation, to properly handle the case
      49                 :            :  * where the controller allows switch connections but drops them a few seconds
      50                 :            :  * later for admission control reasons.  Because of this case, we don't want to
      51                 :            :  * just stop setting up flows when we connect to the controller: if we did,
      52                 :            :  * then new flow setup and existing flows would stop during the duration of
      53                 :            :  * connection to the controller, and thus the whole network would go down for
      54                 :            :  * that period of time.
      55                 :            :  *
      56                 :            :  * So, instead, we add some special cases when we are connected to a
      57                 :            :  * controller, but not yet sure that it has admitted us:
      58                 :            :  *
      59                 :            :  *     - We set up flows immediately ourselves, but simultaneously send out an
      60                 :            :  *       OFPT_PACKET_IN to the controller.  We put a special bogus buffer-id in
      61                 :            :  *       these OFPT_PACKET_IN messages so that duplicate packets don't get sent
      62                 :            :  *       out to the network when the controller replies.
      63                 :            :  *
      64                 :            :  *     - We also send out OFPT_PACKET_IN messages for totally bogus packets
      65                 :            :  *       every so often, in case no real new flows are arriving in the network.
      66                 :            :  *
      67                 :            :  *     - We don't flush the flow table at the time we connect, because this
      68                 :            :  *       could cause network stuttering in a switch with lots of flows or very
      69                 :            :  *       high-bandwidth flows by suddenly throwing lots of packets down to
      70                 :            :  *       userspace.
      71                 :            :  */
      72                 :            : 
      73                 :            : struct fail_open {
      74                 :            :     struct ofproto *ofproto;
      75                 :            :     struct connmgr *connmgr;
      76                 :            :     int last_disconn_secs;
      77                 :            :     long long int next_bogus_packet_in;
      78                 :            :     struct rconn_packet_counter *bogus_packet_counter;
      79                 :            :     bool fail_open_active;
      80                 :            : };
      81                 :            : 
      82                 :            : static void fail_open_recover(struct fail_open *);
      83                 :            : 
      84                 :            : /* Returns the number of seconds of disconnection after which fail-open mode
      85                 :            :  * should activate. */
      86                 :            : static int
      87                 :          0 : trigger_duration(const struct fail_open *fo)
      88                 :            : {
      89         [ #  # ]:          0 :     if (!connmgr_has_controllers(fo->connmgr)) {
      90                 :            :         /* Shouldn't ever arrive here, but if we do, never fail open. */
      91                 :          0 :         return INT_MAX;
      92                 :            :     } else {
      93                 :            :         /* Otherwise, every controller must have a chance to send an
      94                 :            :          * inactivity probe and reconnect before we fail open, so take the
      95                 :            :          * maximum probe interval and multiply by 3:
      96                 :            :          *
      97                 :            :          *  - The first interval is the idle time before sending an inactivity
      98                 :            :          *    probe.
      99                 :            :          *
     100                 :            :          *  - The second interval is the time allowed for a response to the
     101                 :            :          *    inactivity probe.
     102                 :            :          *
     103                 :            :          *  - The third interval is the time allowed to reconnect after no
     104                 :            :          *    response is received.
     105                 :            :          */
     106                 :          0 :         return connmgr_get_max_probe_interval(fo->connmgr) * 3;
     107                 :            :     }
     108                 :            : }
     109                 :            : 
     110                 :            : /* Returns true if 'fo' is currently in fail-open mode, otherwise false. */
     111                 :            : bool
     112                 :          0 : fail_open_is_active(const struct fail_open *fo)
     113                 :            : {
     114                 :          0 :     return fo->last_disconn_secs != 0;
     115                 :            : }
     116                 :            : 
     117                 :            : static void
     118                 :          0 : send_bogus_packet_ins(struct fail_open *fo)
     119                 :            : {
     120                 :            :     struct eth_addr mac;
     121                 :            :     struct dp_packet b;
     122                 :            : 
     123                 :          0 :     dp_packet_init(&b, 128);
     124                 :          0 :     eth_addr_nicira_random(&mac);
     125                 :          0 :     compose_rarp(&b, mac);
     126                 :            : 
     127                 :          0 :     struct ofproto_async_msg am = {
     128                 :            :         .oam = OAM_PACKET_IN,
     129                 :            :         .pin = {
     130                 :            :             .up = {
     131                 :            :                 .public = {
     132                 :          0 :                     .packet = dp_packet_data(&b),
     133                 :          0 :                     .packet_len = dp_packet_size(&b),
     134                 :            :                     .flow_metadata = MATCH_CATCHALL_INITIALIZER,
     135                 :            :                     .flow_metadata.flow.in_port.ofp_port = OFPP_LOCAL,
     136                 :            :                     .flow_metadata.wc.masks.in_port.ofp_port
     137                 :          0 :                     = u16_to_ofp(UINT16_MAX),
     138                 :            :                     .reason = OFPR_NO_MATCH,
     139                 :            :                     .cookie = OVS_BE64_MAX,
     140                 :            :                 },
     141                 :            :             },
     142                 :            :             .max_len = UINT16_MAX,
     143                 :            :         }
     144                 :            :     };
     145                 :          0 :     connmgr_send_async_msg(fo->connmgr, &am);
     146                 :            : 
     147                 :          0 :     dp_packet_uninit(&b);
     148                 :          0 : }
     149                 :            : 
     150                 :            : /* Enter fail-open mode if we should be in it. */
     151                 :            : void
     152                 :          0 : fail_open_run(struct fail_open *fo)
     153                 :            : {
     154                 :          0 :     int disconn_secs = connmgr_failure_duration(fo->connmgr);
     155                 :            : 
     156                 :            :     /* Enter fail-open mode if 'fo' is not in it but should be.  */
     157         [ #  # ]:          0 :     if (disconn_secs >= trigger_duration(fo)) {
     158         [ #  # ]:          0 :         if (!fail_open_is_active(fo)) {
     159         [ #  # ]:          0 :             VLOG_WARN("Could not connect to controller (or switch failed "
     160                 :            :                       "controller's post-connection admission control "
     161                 :            :                       "policy) for %d seconds, failing open", disconn_secs);
     162                 :          0 :             fo->last_disconn_secs = disconn_secs;
     163                 :            : 
     164                 :            :             /* Flush all OpenFlow and datapath flows.  We will set up our
     165                 :            :              * fail-open rule from fail_open_flushed() when
     166                 :            :              * ofproto_flush_flows() calls back to us. */
     167                 :          0 :             ofproto_flush_flows(fo->ofproto);
     168         [ #  # ]:          0 :         } else if (disconn_secs > fo->last_disconn_secs + 60) {
     169         [ #  # ]:          0 :             VLOG_INFO("Still in fail-open mode after %d seconds disconnected "
     170                 :            :                       "from controller", disconn_secs);
     171                 :          0 :             fo->last_disconn_secs = disconn_secs;
     172                 :            :         }
     173                 :            :     }
     174                 :            : 
     175                 :            :     /* Schedule a bogus packet-in if we're connected and in fail-open. */
     176         [ #  # ]:          0 :     if (fail_open_is_active(fo)) {
     177         [ #  # ]:          0 :         if (connmgr_is_any_controller_connected(fo->connmgr)) {
     178                 :          0 :             bool expired = time_msec() >= fo->next_bogus_packet_in;
     179         [ #  # ]:          0 :             if (expired) {
     180                 :          0 :                 send_bogus_packet_ins(fo);
     181                 :            :             }
     182 [ #  # ][ #  # ]:          0 :             if (expired || fo->next_bogus_packet_in == LLONG_MAX) {
     183                 :          0 :                 fo->next_bogus_packet_in = time_msec() + 2000;
     184                 :            :             }
     185                 :            :         } else {
     186                 :          0 :             fo->next_bogus_packet_in = LLONG_MAX;
     187                 :            :         }
     188                 :            :     }
     189                 :            : 
     190                 :          0 : }
     191                 :            : 
     192                 :            : /* If 'fo' is currently in fail-open mode and its rconn has connected to the
     193                 :            :  * controller, exits fail open mode. */
     194                 :            : void
     195                 :          0 : fail_open_maybe_recover(struct fail_open *fo)
     196                 :            :     OVS_EXCLUDED(ofproto_mutex)
     197                 :            : {
     198         [ #  # ]:          0 :     if (fail_open_is_active(fo)
     199         [ #  # ]:          0 :         && connmgr_is_any_controller_admitted(fo->connmgr)) {
     200                 :          0 :         fail_open_recover(fo);
     201                 :            :     }
     202                 :          0 : }
     203                 :            : 
     204                 :            : static void
     205                 :          0 : fail_open_recover(struct fail_open *fo)
     206                 :            :     OVS_EXCLUDED(ofproto_mutex)
     207                 :            : {
     208                 :            :     struct match match;
     209                 :            : 
     210         [ #  # ]:          0 :     VLOG_WARN("No longer in fail-open mode");
     211                 :          0 :     fo->last_disconn_secs = 0;
     212                 :          0 :     fo->next_bogus_packet_in = LLONG_MAX;
     213                 :            : 
     214                 :          0 :     match_init_catchall(&match);
     215                 :          0 :     ofproto_delete_flow(fo->ofproto, &match, FAIL_OPEN_PRIORITY);
     216                 :          0 : }
     217                 :            : 
     218                 :            : void
     219                 :          0 : fail_open_wait(struct fail_open *fo)
     220                 :            : {
     221         [ #  # ]:          0 :     if (fo->next_bogus_packet_in != LLONG_MAX) {
     222                 :          0 :         poll_timer_wait_until(fo->next_bogus_packet_in);
     223                 :            :     }
     224                 :          0 : }
     225                 :            : 
     226                 :            : void
     227                 :          0 : fail_open_flushed(struct fail_open *fo)
     228                 :            :     OVS_EXCLUDED(ofproto_mutex)
     229                 :            : {
     230                 :          0 :     int disconn_secs = connmgr_failure_duration(fo->connmgr);
     231                 :          0 :     bool open = disconn_secs >= trigger_duration(fo);
     232         [ #  # ]:          0 :     if (open) {
     233                 :            :         struct ofpbuf ofpacts;
     234                 :            :         struct match match;
     235                 :            : 
     236                 :            :         /* Set up a flow that matches every packet and directs them to
     237                 :            :          * OFPP_NORMAL. */
     238                 :          0 :         ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
     239                 :          0 :         ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
     240                 :            : 
     241                 :          0 :         match_init_catchall(&match);
     242                 :          0 :         ofproto_add_flow(fo->ofproto, &match, FAIL_OPEN_PRIORITY,
     243                 :          0 :                          ofpacts.data, ofpacts.size);
     244                 :            : 
     245                 :          0 :         ofpbuf_uninit(&ofpacts);
     246                 :            :     }
     247                 :          0 :     fo->fail_open_active = open;
     248                 :          0 : }
     249                 :            : 
     250                 :            : /* Returns the number of fail-open rules currently installed in the flow
     251                 :            :  * table. */
     252                 :            : int
     253                 :          0 : fail_open_count_rules(const struct fail_open *fo)
     254                 :            : {
     255                 :          0 :     return fo->fail_open_active != 0;
     256                 :            : }
     257                 :            : 
     258                 :            : /* Creates and returns a new struct fail_open for 'ofproto' and 'mgr'. */
     259                 :            : struct fail_open *
     260                 :          0 : fail_open_create(struct ofproto *ofproto, struct connmgr *mgr)
     261                 :            : {
     262                 :          0 :     struct fail_open *fo = xmalloc(sizeof *fo);
     263                 :          0 :     fo->ofproto = ofproto;
     264                 :          0 :     fo->connmgr = mgr;
     265                 :          0 :     fo->last_disconn_secs = 0;
     266                 :          0 :     fo->next_bogus_packet_in = LLONG_MAX;
     267                 :          0 :     fo->bogus_packet_counter = rconn_packet_counter_create();
     268                 :          0 :     fo->fail_open_active = false;
     269                 :          0 :     return fo;
     270                 :            : }
     271                 :            : 
     272                 :            : /* Destroys 'fo'. */
     273                 :            : void
     274                 :       5594 : fail_open_destroy(struct fail_open *fo)
     275                 :            :     OVS_EXCLUDED(ofproto_mutex)
     276                 :            : {
     277         [ -  + ]:       5594 :     if (fo) {
     278         [ #  # ]:          0 :         if (fail_open_is_active(fo)) {
     279                 :          0 :             fail_open_recover(fo);
     280                 :            :         }
     281                 :            :         /* We don't own fo->connmgr. */
     282                 :          0 :         rconn_packet_counter_destroy(fo->bogus_packet_counter);
     283                 :          0 :         free(fo);
     284                 :            :     }
     285                 :       5594 : }

Generated by: LCOV version 1.12