]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements BGP route refresh.
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 26 Nov 2009 19:47:59 +0000 (20:47 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 26 Nov 2009 19:47:59 +0000 (20:47 +0100)
doc/bird.sgml
nest/config.Y
nest/proto.c
nest/protocol.h
nest/rt-table.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c

index 9e74470f800de1fb6a3016b818b5c16204568db0..d08b0c170e46bbfebb7187a72e6f233c2b2d7fd2 100644 (file)
@@ -482,9 +482,6 @@ This argument can be omitted if there exists only a single instance.
        number of networks, number of routes before and after filtering). If
        you use <cf/count/ instead, only the statistics will be printed.
 
-       <tag>enable|disable|restart <m/name/|"<m/pattern/"|all</tag>
-       Enable, disable or restart a given protocol instance, instances matching the <cf><m/pattern/</cf> or <cf/all/ instances.
-
        <tag>configure [soft] ["<m/config file/"]</tag>
        Reload configuration from a given file. BIRD will smoothly
        switch itself to the new configuration, protocols are
@@ -496,6 +493,9 @@ This argument can be omitted if there exists only a single instance.
        but new routes would be processed according to the new
        filters.
 
+       <tag>enable|disable|restart <m/name/|"<m/pattern/"|all</tag>
+       Enable, disable or restart a given protocol instance, instances matching the <cf><m/pattern/</cf> or <cf/all/ instances.
+
        <tag/down/
        Shut BIRD down.
 
@@ -944,6 +944,16 @@ for each neighbor using the following configuration parameters:
        attributes to be transparent (for example does not prepend its AS number to
        AS PATH attribute and keep MED attribute). Default: disabled.
 
+       <tag>enable route refresh <m/switch/</tag> When BGP speaker
+       changes its import filter, it has to re-examine all routes
+       received from its neighbor against the new filter. As these
+       routes might not be available, there is a BGP protocol
+       extension Route Refresh (specified in RFC 2918) that allows
+       BGP speaker to request re-advertisment of all routes from its
+       neighbor. This option specifies whether BIRD advertises this
+       capability and accepts such requests. Even when disabled, BIRD
+       can send route refresh requests. Default: on.
+
        <tag>enable as4 <m/switch/</tag> BGP protocol was designed to use 2B AS numbers
        and was extended later to allow 4B AS number. BIRD supports 4B AS extension,
        but by disabling this option it can be persuaded not to advertise it and
index 3d6f1548129a732f368e003c3467642d581acc02..dde20c5a803d49849c264b81f27c916de15b2ddf 100644 (file)
@@ -45,6 +45,7 @@ CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILT
 CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
 CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE)
 CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
+CF_KEYWORDS(RELOAD, REFEED)
 
 CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
        RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@@ -442,6 +443,12 @@ CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]]
 { proto_xxable($2, 1); } ;
 CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
 { proto_xxable($2, 2); } ;
+CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
+{ proto_xxable($2, 3); } ;
+CF_CLI(REFEED, proto_patt, <protocol> | \"<pattern>\" | all, [[Refeed protocol BROKEN]])
+{ proto_xxable($2, 4); } ;
+
+
 
 CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging]])
 CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging]])
index c05db767307278773d60e549b2a4732d69eb45ad..c531d869c6b5e3b059029e0f28c4a6a13aa8128b 100644 (file)
@@ -73,6 +73,10 @@ proto_relink(struct proto *p)
   rem_node(&p->n);
   switch (p->core_state)
     {
+    case FS_HUNGRY:
+      l = &inactive_proto_list;
+      break;
+    case FS_FEEDING:
     case FS_HAPPY:
       l = &active_proto_list;
       break;
@@ -80,7 +84,7 @@ proto_relink(struct proto *p)
       l = &flush_proto_list;
       break;
     default:
-      l = &inactive_proto_list;
+      ASSERT(0);
     }
   proto_enqueue(l, p);
 }
@@ -549,7 +553,7 @@ proto_feed_more(void *P)
 }
 
 static void
-proto_feed(void *P)
+proto_feed_initial(void *P)
 {
   struct proto *p = P;
 
@@ -577,15 +581,44 @@ proto_schedule_flush(struct proto *p)
 }
 
 static void
-proto_schedule_feed(struct proto *p)
+proto_schedule_feed(struct proto *p, int initial)
 {
   DBG("%s: Scheduling meal\n", p->name);
   p->core_state = FS_FEEDING;
   proto_relink(p);
-  p->attn->hook = proto_feed;
+  p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
   ev_schedule(p->attn);
 }
 
+/**
+ * proto_request_feeding - request feeding routes to the protocol
+ * @p: given protocol 
+ *
+ * Sometimes it is needed to send again all routes to the
+ * protocol. This is called feeding and can be requested by this
+ * function. This would cause protocol core state transition
+ * to FS_FEEDING (during feeding) and when completed, it will
+ * switch back to FS_HAPPY. This function can be called even
+ * when feeding is already running, in that case it is restarted.
+ */
+void
+proto_request_feeding(struct proto *p)
+{
+  ASSERT(p->proto_state == PS_UP);
+
+  /* If we are already feeding, we want to restart it */
+  if (p->core_state == FS_FEEDING)
+    {
+      /* Unless feeding is in initial state */
+      if (p->attn->hook == proto_feed_initial)
+       return;
+
+      rt_feed_baby_abort(p);
+    }
+
+  proto_schedule_feed(p, 0);
+}
+
 /**
  * proto_notify_state - notify core about protocol state change
  * @p: protocol the state of which has changed
@@ -635,7 +668,7 @@ proto_notify_state(struct proto *p, unsigned ps)
     case PS_UP:
       ASSERT(ops == PS_DOWN || ops == PS_START);
       ASSERT(cs == FS_HUNGRY);
-      proto_schedule_feed(p);
+      proto_schedule_feed(p, 1);
       break;
     case PS_STOP:
       if ((cs = FS_FEEDING) || (cs == FS_HAPPY))
@@ -798,6 +831,7 @@ proto_xxable(char *pattern, int xx)
              {
                cli_msg(-9, "%s: disabled", p->name);
                p->disabled = 1;
+               proto_rethink_goal(p);
              }
            break;
          case 1:
@@ -807,6 +841,7 @@ proto_xxable(char *pattern, int xx)
              {
                cli_msg(-11, "%s: enabled", p->name);
                p->disabled = 0;
+               proto_rethink_goal(p);
              }
            break;
          case 2:
@@ -817,13 +852,33 @@ proto_xxable(char *pattern, int xx)
                p->disabled = 1;
                proto_rethink_goal(p);
                p->disabled = 0;
+               proto_rethink_goal(p);
                cli_msg(-12, "%s: restarted", p->name);
              }
            break;
+         case 3:
+           // FIXME change msg number
+           if (p->disabled)
+             cli_msg(-8, "%s: already disabled", p->name);
+           else if (p->reload_routes && p->reload_routes(p))
+             cli_msg(-12, "%s: reloading", p->name);
+           else
+             cli_msg(-12, "%s: reload failed", p->name);
+           break;
+         case 4:
+           // FIXME change msg number
+           if (p->disabled)
+             cli_msg(-8, "%s: already disabled", p->name);
+           else
+             {
+               proto_request_feeding(p);
+               cli_msg(-12, "%s: reexport failed", p->name);
+             }
+           break;
+
          default:
            ASSERT(0);
          }
-       proto_rethink_goal(p);
       }
   WALK_PROTO_LIST_END;
   if (!cnt)
index 807b579fc450adf31d906343bc0105870a295526..66e6c43db3389acce943f9a053dd8e599a891d83 100644 (file)
@@ -152,6 +152,9 @@ struct proto {
    *                   It can construct a new rte, add private attributes and
    *                   decide whether the route shall be imported: 1=yes, -1=no,
    *                   0=process it through the import filter set by the user.
+   *      reload_routes   Request protocol to reload all its routes to the core
+   *                   (using rte_update()). Returns: 0=reload cannot be done,
+   *                   1= reload is scheduled and will happen (asynchronously).
    */
 
   void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
@@ -161,6 +164,7 @@ struct proto {
   struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
   void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs);
   int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool);
+  int (*reload_routes)(struct proto *);
 
   /*
    *   Routing entry hooks (called only for rte's belonging to this protocol):
@@ -190,6 +194,7 @@ struct proto {
 void *proto_new(struct proto_config *, unsigned size);
 void *proto_config_new(struct protocol *, unsigned size);
 
+void proto_request_feeding(struct proto *p);
 void proto_show(struct symbol *, int);
 struct proto *proto_get_named(struct symbol *, struct protocol *);
 void proto_xxable(char *, int);
@@ -271,6 +276,10 @@ void proto_notify_state(struct proto *p, unsigned state);
  *             HUNGRY/DOWN --> HUNGRY/START --> HUNGRY/UP -->
  *             FEEDING/UP --> HAPPY/UP --> FLUSHING/STOP|DOWN -->
  *             HUNGRY/STOP|DOWN --> HUNGRY/DOWN
+ *
+ *     Sometimes, protocol might switch from HAPPY/UP to FEEDING/UP 
+ *     if it wants to refeed the routes (for example BGP does so
+ *     as a result of received ROUTE-REFRESH request).
  */
 
 #define FS_HUNGRY 0
index dda0e56b157e562b7396d8cb829196302cda4989..e0cd7971a8eabd53e92b1e42f5c1889c667caf40 100644 (file)
@@ -199,6 +199,14 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
   else
     p->stats.exp_withdraws_received++;
 
+  /* This is a tricky part - we don't know whether route 'old' was
+     exported to protocol 'p' or was filtered by the export filter.
+     We try tu run the export filter to know this to have a correct
+     value in 'old' argument of rt_update (and proper filter value)
+
+     FIXME - this is broken because 'configure soft' may change
+     filters but keep routes */
+
   if (old)
     {
       if (p->out_filter == FILTER_REJECT)
@@ -216,6 +224,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
        }
     }
 
+  /* FIXME - This is broken because of incorrect 'old' value (see above) */
   if (!new && !old)
     return;
 
@@ -1122,6 +1131,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
            ok = 0;
          else if (!ic && d->export_mode > 1)
            {
+             /* FIXME - this shows what should be exported according
+                to current filters, but not what was really exported.
+                'configure soft' command may change the export filter
+                and do not update routes */
+
              if (p1->out_filter == FILTER_REJECT ||
                  p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
                ok = 0;
index cde0276870934129d14dd4a131cc3460613eed44..24cd20238d19d4cf8ea294a8c3e9abae017b6bf1 100644 (file)
@@ -676,6 +676,17 @@ bgp_neigh_notify(neighbor *n)
     }
 }
 
+static int
+bgp_reload_routes(struct proto *P)
+{
+  struct bgp_proto *p = (struct bgp_proto *) P;
+  if (!p->conn || !p->conn->peer_refresh_support)
+    return 0;
+
+  bgp_schedule_packet(p->conn, PKT_ROUTE_REFRESH);
+  return 1;
+}
+
 static void
 bgp_start_locked(struct object_lock *lock)
 {
@@ -792,6 +803,7 @@ bgp_init(struct proto_config *C)
   P->rte_better = bgp_rte_better;
   P->import_control = bgp_import_control;
   P->neigh_notify = bgp_neigh_notify;
+  P->reload_routes = bgp_reload_routes;
   p->cf = c;
   p->local_as = c->local_as;
   p->remote_as = c->remote_as;
index c4d03352c324c941af557d66dd0c50a819f4dabd..59ec9c163d5e9a87b684715a1afb4e682388e469 100644 (file)
@@ -29,6 +29,7 @@ struct bgp_config {
   u32 default_local_pref;              /* Default value for LOCAL_PREF attribute */
   u32 default_med;                     /* Default value for MULTI_EXIT_DISC attribute */
   int capabilities;                    /* Enable capability handshake [RFC3392] */
+  int enable_refresh;                  /* Enable local support for route refresh [RFC2918] */
   int enable_as4;                      /* Enable local support for 4B AS numbers [RFC4893] */
   u32 rr_cluster_id;                   /* Route reflector cluster ID, if different from local ID */
   int rr_client;                       /* Whether neighbor is RR client of me */
@@ -66,6 +67,7 @@ struct bgp_conn {
   int start_state;                     /* protocol start_state snapshot when connection established */
   int want_as4_support;                        /* Connection tries to establish AS4 session */
   int peer_as4_support;                        /* Peer supports 4B AS numbers [RFC4893] */
+  int peer_refresh_support;            /* Peer supports route refresh [RFC2918] */
   unsigned hold_time, keepalive_time;  /* Times calculated from my and neighbor's requirements */
 };
 
@@ -202,6 +204,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
 #define PKT_UPDATE             0x02
 #define PKT_NOTIFICATION       0x03
 #define PKT_KEEPALIVE          0x04
+#define PKT_ROUTE_REFRESH      0x05
 #define PKT_SCHEDULE_CLOSE     0x1f    /* Used internally to schedule socket close */
 
 /* Attributes */
index 35769466ca1856db1c8e13c96ffa4db2b5ea3484..3c73d60e1c4168f8b5924a29f28a6c26b60c8fa0 100644 (file)
@@ -23,7 +23,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
        BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS,
        PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4,
        CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR,
-       DROP, IGNORE)
+       DROP, IGNORE, ROUTE, REFRESH)
 
 CF_GRAMMAR
 
@@ -40,6 +40,7 @@ bgp_proto_start: proto_start BGP {
      BGP_CFG->error_amnesia_time = 300;
      BGP_CFG->error_delay_time_min = 60;
      BGP_CFG->error_delay_time_max = 300;
+     BGP_CFG->enable_refresh = 1;
      BGP_CFG->enable_as4 = bgp_as4_support;
      BGP_CFG->capabilities = 2;
      BGP_CFG->advertise_ipv4 = 1;
@@ -77,6 +78,7 @@ bgp_proto:
  | bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; } 
  | bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
  | bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
+ | bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; }
  | bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
  | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
  | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
index 4176c9fe1f10cd9b2c31a91387bd36103ab4475a..91b47927347b175c314d5b3a0b248e46b8b331ab 100644 (file)
@@ -63,6 +63,14 @@ bgp_put_cap_ipv4(struct bgp_conn *conn UNUSED, byte *buf)
 }
 #endif
 
+static byte *
+bgp_put_cap_rr(struct bgp_conn *conn UNUSED, byte *buf)
+{
+  *buf++ = 2;          /* Capability 2: Support for route refresh */
+  *buf++ = 0;          /* Capability data length */
+  return buf;
+}
+
 static byte *
 bgp_put_cap_as4(struct bgp_conn *conn, byte *buf)
 {
@@ -105,6 +113,9 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
   cap = bgp_put_cap_ipv6(conn, cap);
 #endif
 
+  if (p->cf->enable_refresh)
+    cap = bgp_put_cap_rr(conn, cap);
+
   if (conn->want_as4_support)
     cap = bgp_put_cap_as4(conn, cap);
 
@@ -386,6 +397,24 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
 
 #endif
 
+static byte *
+bgp_create_route_refresh(struct bgp_conn *conn, byte *buf)
+{
+  struct bgp_proto *p = conn->bgp;
+  BGP_TRACE(D_PACKETS, "Sending ROUTE-REFRESH");
+
+#ifdef IPV6
+  *buf++ = 0;          /* AFI IPv6 */
+  *buf++ = BGP_AF_IPV6;
+#else
+  *buf++ = 0;          /* AFI IPv4 */
+  *buf++ = BGP_AF_IPV4;
+#endif
+  *buf++ = 0;          /* RFU */
+  *buf++ = 1;          /* and SAFI 1 */
+  return buf;
+}
+
 static void
 bgp_create_header(byte *buf, unsigned int len, unsigned int type)
 {
@@ -447,6 +476,12 @@ bgp_fire_tx(struct bgp_conn *conn)
       type = PKT_OPEN;
       end = bgp_create_open(conn, pkt);
     }
+  else if (s & (1 << PKT_ROUTE_REFRESH))
+    {
+      s &= ~(1 << PKT_ROUTE_REFRESH);
+      type = PKT_ROUTE_REFRESH;
+      end = bgp_create_route_refresh(conn, pkt);
+    }
   else if (s & (1 << PKT_UPDATE))
     {
       end = bgp_create_update(conn, pkt);
@@ -517,6 +552,11 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
 
       switch (opt[0])
        {
+       case 2:
+         if (cl != 0)
+           goto err;
+         conn->peer_refresh_support = 1;
+         break;
        case 65:
          if (cl != 4)
            goto err;
@@ -1084,6 +1124,30 @@ bgp_rx_keepalive(struct bgp_conn *conn)
     }
 }
 
+static void
+bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, int len)
+{
+  struct bgp_proto *p = conn->bgp;
+
+  BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
+
+  if (conn->state != BS_ESTABLISHED)
+    { bgp_error(conn, 5, 0, NULL, 0); return; }
+
+  if (!p->cf->enable_refresh)
+    { bgp_error(conn, 1, 3, pkt+18, 1); return; }
+
+  if (len != (BGP_HEADER_LENGTH + 4))
+    { bgp_error(conn, 1, 2, pkt+16, 2); return; }
+
+  /* FIXME - we ignore AFI/SAFI values, as we support
+     just one value and even an error code for an invalid
+     request is not defined */
+
+  proto_request_feeding(&p->p);
+}
+
+
 /**
  * bgp_rx_packet - handle a received packet
  * @conn: BGP connection
@@ -1103,6 +1167,7 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
     case PKT_UPDATE:           return bgp_rx_update(conn, pkt, len);
     case PKT_NOTIFICATION:      return bgp_rx_notification(conn, pkt, len);
     case PKT_KEEPALIVE:                return bgp_rx_keepalive(conn);
+    case PKT_ROUTE_REFRESH:    return bgp_rx_route_refresh(conn, pkt, len);
     default:                   bgp_error(conn, 1, 3, pkt+18, 1);
     }
 }