]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: Dynamic BGP
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Mon, 8 Apr 2019 15:05:07 +0000 (17:05 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 30 Apr 2019 11:32:39 +0000 (13:32 +0200)
Support for dynamically spawning BGP protocols for incoming connections.
Use 'neighbor range' to specify range of valid neighbor addresses, then
incoming connections from these addresses spawn new BGP instances.

conf/cf-lex.l
conf/conf.h
nest/proto.c
nest/protocol.h
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
sysdep/unix/io.c

index 9bbb3660a45efac9b9f4127265972da58aea0da6..63c76940b686d2a91f62c918db58118fb1d1b2b4 100644 (file)
@@ -87,7 +87,7 @@ HASH_DEFINE_REHASH_FN(SYM, struct symbol)
 HASH(struct keyword) kw_hash;
 
 
-static struct sym_scope *conf_this_scope;
+struct sym_scope *conf_this_scope;
 
 linpool *cfg_mem;
 
@@ -673,7 +673,8 @@ cf_lex_init(int is_cli, struct config *c)
   else
     BEGIN(INITIAL);
 
-  conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
+  c->root_scope = cfg_allocz(sizeof(struct sym_scope));
+  conf_this_scope = c->root_scope;
   conf_this_scope->active = 1;
 }
 
index 427569fd94bb485b923a435df99659554d388c7b..354a4da8b1c976d115f5dd0d820e5e873980aa68 100644 (file)
@@ -52,6 +52,7 @@ struct config {
   int file_fd;                         /* File descriptor of main configuration file */
   HASH(struct symbol) sym_hash;                /* Lexer: symbol hash table */
   struct config *fallback;             /* Link to regular config for CLI parsing */
+  struct sym_scope *root_scope;                /* Scope for root symbols */
   int obstacle_count;                  /* Number of items blocking freeing of this config */
   int shutdown;                                /* This is a pseudo-config for daemon shutdown */
   btime load_time;                     /* When we've got this configuration */
@@ -152,6 +153,8 @@ struct include_file_stack {
 
 extern struct include_file_stack *ifs;
 
+extern struct sym_scope *conf_this_scope;
+
 int cf_lex(void);
 void cf_lex_init(int is_cli, struct config *c);
 void cf_lex_unwind(void);
index d4a333d077adb9a5814720fe3eab6b870ce8e784..415471c4bc473f7b63384a7c3540b205de9fe289 100644 (file)
@@ -874,6 +874,28 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
   dest->protocol->copy_config(dest, src);
 }
 
+void
+proto_clone_config(struct symbol *sym, struct proto_config *parent)
+{
+  struct proto_config *cf = proto_config_new(parent->protocol, SYM_PROTO);
+  proto_copy_config(cf, parent);
+  cf->name = sym->name;
+  cf->proto = NULL;
+  cf->parent = parent;
+
+  sym->class = cf->class;
+  sym->def = cf;
+}
+
+static void
+proto_undef_clone(struct symbol *sym, struct proto_config *cf)
+{
+  rem_node(&cf->n);
+
+  sym->class = SYM_VOID;
+  sym->def = NULL;
+}
+
 /**
  * protos_preconfig - pre-configuration processing
  * @c: new configuration
@@ -973,6 +995,24 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
     {
       p = oc->proto;
       sym = cf_find_symbol(new, oc->name);
+
+      /* Handle dynamic protocols */
+      if (!sym && oc->parent && !new->shutdown)
+      {
+       struct symbol *parsym = cf_find_symbol(new, oc->parent->name);
+       if (parsym && parsym->class == SYM_PROTO)
+       {
+         /* This is hack, we would like to share config, but we need to copy it now */
+         new_config = new;
+         cfg_mem = new->mem;
+         conf_this_scope = new->root_scope;
+         sym = cf_get_symbol(oc->name);
+         proto_clone_config(sym, parsym->def);
+         new_config = NULL;
+         cfg_mem = NULL;
+       }
+      }
+
       if (sym && sym->class == SYM_PROTO && !new->shutdown)
       {
        /* Found match, let's check if we can smoothly switch to new configuration */
@@ -984,6 +1024,12 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
        if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
          continue;
 
+       if (nc->parent)
+       {
+         proto_undef_clone(sym, nc);
+         goto remove;
+       }
+
        /* Unsuccessful, we will restart it */
        if (!p->disabled && !nc->disabled)
          log(L_INFO "Restarting protocol %s", p->name);
@@ -997,6 +1043,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
       }
       else if (!new->shutdown)
       {
+      remove:
        log(L_INFO "Removing protocol %s", p->name);
        p->down_code = PDC_CF_REMOVE;
        p->cf_new = NULL;
@@ -1105,6 +1152,15 @@ proto_rethink_goal(struct proto *p)
   }
 }
 
+struct proto *
+proto_spawn(struct proto_config *cf, uint disabled)
+{
+  struct proto *p = proto_init(cf, TAIL(proto_list));
+  p->disabled = disabled;
+  proto_rethink_goal(p);
+  return p;
+}
+
 
 /**
  * DOC: Graceful restart recovery
index 56d66ed560fa7e9b65ea9bba42e2d2c7edacdc9d..b26d9cc1f652cc3b5aaa970d4358d77691bd3adc 100644 (file)
@@ -89,6 +89,7 @@ void protos_build(void);
 void proto_build(struct protocol *);
 void protos_preconfig(struct config *);
 void protos_commit(struct config *new, struct config *old, int force_restart, int type);
+struct proto * proto_spawn(struct proto_config *cf, uint disabled);
 void protos_dump_all(void);
 
 #define GA_UNKNOWN     0               /* Attribute not recognized */
@@ -113,6 +114,7 @@ struct proto_config {
   struct config *global;               /* Global configuration data */
   struct protocol *protocol;           /* Protocol */
   struct proto *proto;                 /* Instance we've created */
+  struct proto_config *parent;         /* Parent proto_config for dynamic protocols */
   char *name;
   char *dsc;
   int class;                           /* SYM_PROTO or SYM_TEMPLATE */
@@ -263,6 +265,7 @@ struct proto_spec {
 void *proto_new(struct proto_config *);
 void *proto_config_new(struct protocol *, int class);
 void proto_copy_config(struct proto_config *dest, struct proto_config *src);
+void proto_clone_config(struct symbol *sym, struct proto_config *parent);
 void proto_set_message(struct proto *p, char *msg, int len);
 
 void graceful_restart_recovery(void);
index c332a836ad1f66f81b3bdf2a25efb741e0d6135f..a5c06783c065f90b30895a9d72c31a0305d99e82 100644 (file)
@@ -129,6 +129,9 @@ static list bgp_sockets;            /* Global list of listening sockets */
 
 static void bgp_connect(struct bgp_proto *p);
 static void bgp_active(struct bgp_proto *p);
+static void bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn);
+static void bgp_setup_sk(struct bgp_conn *conn, sock *s);
+static void bgp_send_open(struct bgp_conn *conn);
 static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
 
 static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
@@ -149,7 +152,7 @@ bgp_open(struct bgp_proto *p)
   struct bgp_socket *bs = NULL;
   struct iface *ifa = p->cf->strict_bind ? p->cf->iface : NULL;
   ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
-    (ipa_is_ip4(p->cf->remote_ip) ? IPA_NONE4 : IPA_NONE6);
+    (p->ipv4 ? IPA_NONE4 : IPA_NONE6);
   uint port = p->cf->local_port;
 
   /* FIXME: Add some global init? */
@@ -272,8 +275,17 @@ bgp_startup(struct bgp_proto *p)
   BGP_TRACE(D_EVENTS, "Started");
   p->start_state = BSS_CONNECT;
 
-  if (!p->cf->passive)
+  if (!p->passive)
     bgp_active(p);
+
+  if (p->postponed_sk)
+  {
+    /* Apply postponed incoming connection */
+    bgp_setup_conn(p, &p->incoming_conn);
+    bgp_setup_sk(&p->incoming_conn, p->postponed_sk);
+    bgp_send_open(&p->incoming_conn);
+    p->postponed_sk = NULL;
+  }
 }
 
 static void
@@ -456,7 +468,7 @@ bgp_decision(void *vp)
   if ((p->p.proto_state == PS_START) &&
       (p->outgoing_conn.state == BS_IDLE) &&
       (p->incoming_conn.state != BS_OPENCONFIRM) &&
-      !p->cf->passive)
+      !p->passive)
     bgp_active(p);
 
   if ((p->p.proto_state == PS_STOP) &&
@@ -465,6 +477,29 @@ bgp_decision(void *vp)
     bgp_down(p);
 }
 
+static struct bgp_proto *
+bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
+{
+  struct symbol *sym;
+  char fmt[SYM_MAX_LEN];
+
+  bsprintf(fmt, "%s%%0%dd", pp->cf->dynamic_name, pp->cf->dynamic_name_digits);
+
+  /* This is hack, we would like to share config, but we need to copy it now */
+  new_config = config;
+  cfg_mem = config->mem;
+  conf_this_scope = config->root_scope;
+  sym = cf_default_name(fmt, &(pp->dynamic_name_counter));
+  proto_clone_config(sym, pp->p.cf);
+  new_config = NULL;
+  cfg_mem = NULL;
+
+  /* Just pass remote_ip to bgp_init() */
+  ((struct bgp_config *) sym->def)->remote_ip = remote_ip;
+
+  return (void *) proto_spawn(sym->def, 0);
+}
+
 void
 bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len)
 {
@@ -1088,6 +1123,9 @@ err:
   return;
 }
 
+static inline int bgp_is_dynamic(struct bgp_proto *p)
+{ return ipa_zero(p->remote_ip); }
+
 /**
  * bgp_find_proto - find existing proto for incoming connection
  * @sk: TCP socket
@@ -1096,6 +1134,7 @@ err:
 static struct bgp_proto *
 bgp_find_proto(sock *sk)
 {
+  struct bgp_proto *best = NULL;
   struct bgp_proto *p;
 
   /* sk->iface is valid only if src or dst address is link-local */
@@ -1103,13 +1142,20 @@ bgp_find_proto(sock *sk)
 
   WALK_LIST(p, proto_list)
     if ((p->p.proto == &proto_bgp) &&
-       (p->sock == sk->data) &&
-       ipa_equal(p->cf->remote_ip, sk->daddr) &&
+       (ipa_equal(p->remote_ip, sk->daddr) || bgp_is_dynamic(p)) &&
+       (!p->cf->remote_range || ipa_in_netX(sk->daddr, p->cf->remote_range)) &&
+       (p->p.vrf == sk->vrf) &&
+       (p->cf->local_port == sk->sport) &&
        (!link || (p->cf->iface == sk->iface)) &&
        (ipa_zero(p->cf->local_ip) || ipa_equal(p->cf->local_ip, sk->saddr)))
-      return p;
+    {
+      best = p;
 
-  return NULL;
+      if (!bgp_is_dynamic(p))
+       break;
+    }
+
+  return best;
 }
 
 /**
@@ -1188,6 +1234,16 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
     sk_reallocate(sk);
   }
 
+  /* For dynamic BGP, spawn new instance and postpone the socket */
+  if (bgp_is_dynamic(p))
+  {
+    p = bgp_spawn(p, sk->daddr);
+    p->postponed_sk = sk;
+    rmove(sk, p->p.pool);
+    return 0;
+  }
+
+  rmove(sk, p->p.pool);
   bgp_setup_conn(p, &p->incoming_conn);
   bgp_setup_sk(&p->incoming_conn, sk);
   bgp_send_open(&p->incoming_conn);
@@ -1306,7 +1362,7 @@ bgp_bfd_notify(struct bfd_request *req)
 static void
 bgp_update_bfd(struct bgp_proto *p, int use_bfd)
 {
-  if (use_bfd && !p->bfd_req)
+  if (use_bfd && !p->bfd_req && !bgp_is_dynamic(p))
     p->bfd_req = bfd_request_session(p->p.pool, p->remote_ip, p->local_ip,
                                     p->cf->multihop ? NULL : p->neigh->iface,
                                     bgp_bfd_notify, p);
@@ -1398,7 +1454,7 @@ bgp_start_locked(struct object_lock *lock)
 
   DBG("BGP: Got lock\n");
 
-  if (cf->multihop)
+  if (cf->multihop || bgp_is_dynamic(p))
   {
     /* Multi-hop sessions do not use neighbor entries */
     bgp_initiate(p);
@@ -1433,20 +1489,26 @@ bgp_start(struct proto *P)
   const struct bgp_config *cf = p->cf;
 
   p->local_ip = cf->local_ip;
-  p->remote_ip = cf->remote_ip;
   p->local_as = cf->local_as;
   p->remote_as = cf->remote_as;
   p->public_as = cf->local_as;
 
+  /* For dynamic BGP childs, remote_ip is already set */
+  if (ipa_nonzero(cf->remote_ip))
+    p->remote_ip = cf->remote_ip;
+
   /* Confederation ID is used for truly external peers */
   if (p->cf->confederation && !p->is_interior)
     p->public_as = cf->confederation;
 
+  p->passive = cf->passive || bgp_is_dynamic(p);
+
   p->start_state = BSS_PREPARE;
   p->outgoing_conn.state = BS_IDLE;
   p->incoming_conn.state = BS_IDLE;
   p->neigh = NULL;
   p->bfd_req = NULL;
+  p->postponed_sk = NULL;
   p->gr_ready = 0;
   p->gr_active_num = 0;
 
@@ -1588,6 +1650,17 @@ bgp_init(struct proto_config *CF)
   p->rs_client = cf->rs_client;
   p->rr_client = cf->rr_client;
 
+  p->ipv4 = ipa_nonzero(cf->remote_ip) ?
+    ipa_is_ip4(cf->remote_ip) :
+    (cf->remote_range && (cf->remote_range->type == NET_IP4));
+
+  p->remote_ip = cf->remote_ip;
+  p->remote_as = cf->remote_as;
+
+  /* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */
+  if (cf->c.parent)
+    cf->remote_ip = IPA_NONE;
+
   /* Add all channels */
   struct bgp_channel_config *cc;
   WALK_LIST(cc, CF->channels)
@@ -1788,9 +1861,12 @@ bgp_postconfig(struct proto_config *CF)
   if (!cf->local_as)
     cf_error("Local AS number must be set");
 
-  if (ipa_zero(cf->remote_ip))
+  if (ipa_zero(cf->remote_ip) && !cf->remote_range)
     cf_error("Neighbor must be configured");
 
+  if (ipa_zero(cf->local_ip) && cf->strict_bind)
+    cf_error("Local address must be configured for strict bind");
+
   if (!cf->remote_as && !cf->peer_type)
     cf_error("Remote AS number (or peer type) must be set");
 
@@ -1921,7 +1997,10 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
                     // password item is last and must be checked separately
                     OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
     && ((!old->password && !new->password)
-       || (old->password && new->password && !strcmp(old->password, new->password)));
+       || (old->password && new->password && !strcmp(old->password, new->password)))
+    && net_equal(old->remote_range, new->remote_range)
+    && !strcmp(old->dynamic_name, new->dynamic_name)
+    && (old->dynamic_name_digits == new->dynamic_name_digits);
 
   /* FIXME: Move channel reconfiguration to generic protocol code ? */
   struct channel *C, *C2;
@@ -1951,6 +2030,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
   if (same)
     p->cf = new;
 
+  /* Reset name counter */
+  p->dynamic_name_counter = 0;
+
   return same;
 }
 
@@ -2081,7 +2163,7 @@ bgp_state_dsc(struct bgp_proto *p)
     return "Down";
 
   int state = MAX(p->incoming_conn.state, p->outgoing_conn.state);
-  if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->cf->passive)
+  if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->passive)
     return "Passive";
 
   return bgp_state_names[state];
@@ -2257,8 +2339,13 @@ bgp_show_proto_info(struct proto *P)
   struct bgp_proto *p = (struct bgp_proto *) P;
 
   cli_msg(-1006, "  BGP state:          %s", bgp_state_dsc(p));
-  cli_msg(-1006, "    Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
-  cli_msg(-1006, "    Neighbor AS:      %u", p->cf->remote_as ?: p->remote_as);
+
+  if (bgp_is_dynamic(p) && p->cf->remote_range)
+    cli_msg(-1006, "    Neighbor range:   %N", p->cf->remote_range);
+  else
+    cli_msg(-1006, "    Neighbor address: %I%J", p->remote_ip, p->cf->iface);
+
+  cli_msg(-1006, "    Neighbor AS:      %u", p->remote_as);
 
   if (p->gr_active_num)
     cli_msg(-1006, "    Neighbor graceful restart active");
index 4d24e4fa3678872441ef3281f39f257e3ee9857b..d8c9fe94ea76b492442435a5b466842e202c2a72 100644 (file)
@@ -124,6 +124,9 @@ struct bgp_config {
   u32 disable_after_cease;             /* Disable it when cease is received, bitfield */
 
   char *password;                      /* Password used for MD5 authentication */
+  net_addr *remote_range;              /* Allowed neighbor range for dynamic BGP */
+  char *dynamic_name;                  /* Name pattern for dynamic BGP */
+  int dynamic_name_digits;             /* Minimum number of digits for dynamic names */
   int check_link;                      /* Use iface link state for liveness detection */
   int bfd;                             /* Use BFD for liveness detection */
 };
@@ -270,12 +273,14 @@ struct bgp_proto {
   u32 local_id;                                /* BGP identifier of this router */
   u32 remote_id;                       /* BGP identifier of the neighbor */
   u32 rr_cluster_id;                   /* Route reflector cluster ID */
-  int start_state;                     /* Substates that partitions BS_START */
+  u8 start_state;                      /* Substates that partitions BS_START */
   u8 is_internal;                      /* Internal BGP session (local_as == remote_as) */
   u8 is_interior;                      /* Internal or intra-confederation BGP session */
   u8 as4_session;                      /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
   u8 rr_client;                                /* Whether neighbor is RR client of me */
   u8 rs_client;                                /* Whether neighbor is RS client of me */
+  u8 ipv4;                             /* Use IPv4 connection, i.e. remote_ip is IPv4 */
+  u8 passive;                          /* Do not initiate outgoing connection */
   u8 route_refresh;                    /* Route refresh allowed to send [RFC 2918] */
   u8 enhanced_refresh;                 /* Enhanced refresh is negotiated [RFC 7313] */
   u8 gr_ready;                         /* Neighbor could do graceful restart */
@@ -292,10 +297,12 @@ struct bgp_proto {
   struct neighbor *neigh;              /* Neighbor entry corresponding to remote ip, NULL if multihop */
   struct bgp_socket *sock;             /* Shared listening socket */
   struct bfd_request *bfd_req;         /* BFD request, if BFD is used */
+  struct birdsock *postponed_sk;       /* Postponed incoming socket for dynamic BGP */
   ip_addr link_addr;                   /* Link-local version of local_ip */
   event *event;                                /* Event for respawning and shutting process */
   timer *startup_timer;                        /* Timer used to delay protocol startup due to previous errors (startup_delay) */
   timer *gr_timer;                     /* Timer waiting for reestablishment after graceful restart */
+  int dynamic_name_counter;            /* Counter for dynamic BGP names */
   uint startup_delay;                  /* Delay (in seconds) of protocol startup due to previous errors */
   btime last_proto_error;              /* Time of last error that leads to protocol stop */
   u8 last_error_class;                         /* Error class of last error */
index c9a6af96265a23d17b2fbb2c474e06bd721904e2..bbc7d9a4ac20a22eca51adfa57619d42563bf8f0 100644 (file)
@@ -29,7 +29,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
        SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
        GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
        STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
-       LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL)
+       LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL,
+       DYNAMIC, RANGE, NAME, DIGITS)
 
 %type <i> bgp_nh
 %type <i32> bgp_afi
@@ -68,6 +69,7 @@ bgp_proto_start: proto_start BGP {
      BGP_CFG->llgr_mode = -1;
      BGP_CFG->llgr_time = 3600;
      BGP_CFG->setkey = 1;
+     BGP_CFG->dynamic_name = "dynbgp";
      BGP_CFG->check_link = -1;
    }
  ;
@@ -120,11 +122,18 @@ bgp_proto:
    }
  | bgp_proto NEIGHBOR bgp_nbr_opts ';'
  | bgp_proto NEIGHBOR ipa ipa_scope bgp_nbr_opts ';' {
-     if (ipa_nonzero(BGP_CFG->remote_ip))
+     if (ipa_nonzero(BGP_CFG->remote_ip) || BGP_CFG->remote_range)
        cf_error("Only one neighbor per BGP instance is allowed");
      BGP_CFG->remote_ip = $3;
      if ($4) BGP_CFG->iface = $4;
    }
+ | bgp_proto NEIGHBOR RANGE net_ip bgp_nbr_opts ';' {
+     if (ipa_nonzero(BGP_CFG->remote_ip) || BGP_CFG->remote_range)
+       cf_error("Only one neighbor per BGP instance is allowed");
+     net_addr *n = cfg_alloc($4.length);
+     net_copy(n, &($4));
+     BGP_CFG->remote_range = n;
+   }
  | bgp_proto INTERFACE TEXT ';' { BGP_CFG->iface = if_get_by_name($3); }
  | bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; }
  | bgp_proto RR CLIENT bool ';' { BGP_CFG->rr_client = $4; }
@@ -136,6 +145,12 @@ bgp_proto:
  | bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
  | bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
  | bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
+ | bgp_proto DYNAMIC NAME text ';' {
+     if (strchr($4, '%')) cf_error("Forbidden character '%%' in dynamic name");
+     if (strlen($4) > (SYM_MAX_LEN - 16)) cf_error("Dynamic name too long");
+     BGP_CFG->dynamic_name = $4;
+   }
+ | bgp_proto DYNAMIC NAME DIGITS expr ';' { BGP_CFG->dynamic_name_digits = $5; if ($5>10) cf_error("Dynamic name digits must be at most 10"); }
  | bgp_proto STRICT BIND bool ';' { BGP_CFG->strict_bind = $4; }
  | bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
  | bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }
index d1d86e3b15cf70c3febc524aa377df7384fe74ca..3a1e22c5fd2da7d9389e6cc00b7d54ca59cc22e8 100644 (file)
@@ -1082,6 +1082,7 @@ sk_passive_connected(sock *s, int type)
   t->fd = fd;
   t->ttl = s->ttl;
   t->tos = s->tos;
+  t->vrf = s->vrf;
   t->rbsize = s->rbsize;
   t->tbsize = s->tbsize;