Merge branch 'rfc4760' into fdn-mods
authorBenjamin Cama <benoar@dolka.fr>
Fri, 29 Jul 2011 11:04:31 +0000 (13:04 +0200)
committerBenjamin Cama <benoar@dolka.fr>
Fri, 29 Jul 2011 11:04:31 +0000 (13:04 +0200)
1  2 
bgp.c
bgp.h
etc/startup-config.default
l2tpns.c
l2tpns.h

diff --combined bgp.c
--- 1/bgp.c
--- 2/bgp.c
+++ b/bgp.c
@@@ -32,8 -32,11 +32,11 @@@ static void bgp_set_retry(struct bgp_pe
  static void bgp_cidr(in_addr_t ip, in_addr_t mask, struct bgp_ip_prefix *pfx);
  static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head,
      struct bgp_route_list *new);
+ static struct bgp_route6_list *bgp_insert_route6(struct bgp_route6_list *head,
+     struct bgp_route6_list *new);
  
  static void bgp_free_routes(struct bgp_route_list *routes);
+ static void bgp_free_routes6(struct bgp_route6_list *routes);
  static char const *bgp_msg_type_str(uint8_t type);
  static int bgp_connect(struct bgp_peer *peer);
  static int bgp_handle_connect(struct bgp_peer *peer);
@@@ -43,11 -46,13 +46,13 @@@ static int bgp_handle_input(struct bgp_
  static int bgp_send_open(struct bgp_peer *peer);
  static int bgp_send_keepalive(struct bgp_peer *peer);
  static int bgp_send_update(struct bgp_peer *peer);
+ static int bgp_send_update6(struct bgp_peer *peer);
  static int bgp_send_notification(struct bgp_peer *peer, uint8_t code,
      uint8_t subcode);
  
  static uint16_t our_as;
  static struct bgp_route_list *bgp_routes = 0;
+ static struct bgp_route6_list *bgp_routes6 = 0;
  
  int bgp_configured = 0;
  struct bgp_peer *bgp_peers = 0;
@@@ -88,6 -93,7 +93,7 @@@ int bgp_setup(int as
        return 0;
  
      bgp_routes = 0;
+     bgp_routes6 = 0;
      bgp_configured = 0; /* set by bgp_start */
  
      return 1;
  
  /* start connection with a peer */
  int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive,
 -    int hold, int enable)
 +    int hold, struct in_addr update_source, int enable)
  {
      struct hostent *h;
      int ibgp;
      }
  
      memcpy(&peer->addr, h->h_addr, sizeof(peer->addr));
 +    peer->source_addr = update_source.s_addr;
      peer->as = as > 0 ? as : our_as;
      ibgp = peer->as == our_as;
  
  
      ADD_ATTRIBUTE();
  
-     /* NEXT_HOP */
-     a.flags = BGP_PATH_ATTR_FLAG_TRANS;
-     a.code = BGP_PATH_ATTR_CODE_NEXT_HOP;
-     if (config->nexthop_address)
-     {
-       ip = config->nexthop_address;
-     }
-     else
-     {
-       ip = my_address; /* we're it */
-     }
-     a.data.s.len = sizeof(ip);
-     memcpy(a.data.s.value, &ip, sizeof(ip));
-     ADD_ATTRIBUTE();
      /* MULTI_EXIT_DISC */
      a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL;
      a.code = BGP_PATH_ATTR_CODE_MULTI_EXIT_DISC;
  
      ADD_ATTRIBUTE();
  
 -    ip = my_address; /* we're it */
+     /* remember the len before adding NEXT_HOP */
+     peer->path_attr_len_without_nexthop = peer->path_attr_len;
+     /* NEXT_HOP */
+     a.flags = BGP_PATH_ATTR_FLAG_TRANS;
+     a.code = BGP_PATH_ATTR_CODE_NEXT_HOP;
++    if (config->nexthop_address)
++    {
++      ip = config->nexthop_address;
++    }
++    else
++    {
++      ip = my_address; /* we're it */
++    }
+     a.data.s.len = sizeof(ip);
+     memcpy(a.data.s.value, &ip, sizeof(ip));
+     ADD_ATTRIBUTE();
      if (!(peer->path_attrs = malloc(peer->path_attr_len)))
      {
        LOG(0, 0, 0, "Can't allocate path_attrs for %s (%s)\n",
  
      memcpy(peer->path_attrs, path_attrs, peer->path_attr_len);
  
+     /* multiprotocol attributes initialization */
+     if (config->ipv6_prefix.s6_addr[0])
+     {
+       struct bgp_attr_mp_reach_nlri_partial mp_reach_nlri_partial;
+       struct bgp_attr_mp_unreach_nlri_partial mp_unreach_nlri_partial;
+       a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL;
+       a.code = BGP_PATH_ATTR_CODE_MP_REACH_NLRI;
+       a.data.s.len = 0; /* will be set on UPDATE */
+       mp_reach_nlri_partial.afi = htons(BGP_MP_AFI_IPv6);
+       mp_reach_nlri_partial.safi = BGP_MP_SAFI_UNICAST;
+       mp_reach_nlri_partial.reserved = 0;
+       mp_reach_nlri_partial.next_hop_len = 16;
+       /* use the defined nexthop6, or our address in ipv6_prefix */
+       if (config->nexthop6_address.s6_addr[0])
+           memcpy(&mp_reach_nlri_partial.next_hop,
+                   &config->nexthop6_address.s6_addr, 16);
+       else
+       {
+           /* our address is ipv6prefix::1 */
+           memcpy(&mp_reach_nlri_partial.next_hop,
+                   &config->ipv6_prefix.s6_addr, 16);
+           mp_reach_nlri_partial.next_hop[15] = 1;
+       }
+       memcpy(&a.data.s.value, &mp_reach_nlri_partial,
+               sizeof(struct bgp_attr_mp_reach_nlri_partial));
+       memcpy(&peer->mp_reach_nlri_partial, &a,
+               BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE);
+       a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL | BGP_PATH_ATTR_FLAG_EXTLEN;
+       a.code = BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI;
+       a.data.e.len = 0; /* will be set on UPDATE */
+       mp_unreach_nlri_partial.afi = htons(BGP_MP_AFI_IPv6);
+       mp_unreach_nlri_partial.safi = BGP_MP_SAFI_UNICAST;
+       memcpy(&a.data.e.value, &mp_unreach_nlri_partial,
+               sizeof(struct bgp_attr_mp_unreach_nlri_partial));
+       memcpy(&peer->mp_unreach_nlri_partial, &a,
+               BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE);
+     }
+     peer->mp_handling = HandlingUnknown;
      LOG(4, 0, 0, "Initiating BGP connection to %s (routing %s)\n",
        name, enable ? "enabled" : "suspended");
  
@@@ -275,6 -323,8 +331,8 @@@ static void bgp_clear(struct bgp_peer *
  
      bgp_free_routes(peer->routes);
      peer->routes = 0;
+     bgp_free_routes6(peer->routes6);
+     peer->routes6 = 0;
  
      peer->outbuf->packet.header.len = 0;
      peer->outbuf->done = 0;
@@@ -382,6 -432,33 +440,33 @@@ static struct bgp_route_list *bgp_inser
      return head;
  }
  
+ /* insert route6 into list; sorted */
+ static struct bgp_route6_list *bgp_insert_route6(struct bgp_route6_list *head,
+     struct bgp_route6_list *new)
+ {
+     struct bgp_route6_list *p = head;
+     struct bgp_route6_list *e = 0;
+     while (p && memcmp(&p->dest, &new->dest, sizeof(p->dest)) < 0)
+     {
+       e = p;
+       p = p->next;
+     }
+     if (e)
+     {
+       new->next = e->next;
+       e->next = new;
+     }
+     else
+     {
+       new->next = head;
+       head = new;
+     }
+     return head;
+ }
  /* add route to list for peers */
  /*
   * Note:  this doesn't do route aggregation, nor drop routes if a less
@@@ -434,6 -511,59 +519,59 @@@ int bgp_add_route(in_addr_t ip, in_addr
      return 1;
  }
  
+ /* add route to list for peers */
+ /*
+  * Note: same provisions as above
+  */
+ int bgp_add_route6(struct in6_addr ip, int prefixlen)
+ {
+     struct bgp_route6_list *r = bgp_routes6;
+     struct bgp_route6_list add;
+     int i;
+     char ipv6addr[INET6_ADDRSTRLEN];
+     memcpy(&add.dest.prefix, &ip.s6_addr, 16);
+     add.dest.len = prefixlen;
+     add.next = 0;
+     /* check for duplicate */
+     while (r)
+     {
+       i = memcmp(&r->dest, &add.dest, sizeof(r->dest));
+       if (!i)
+           return 1; /* already covered */
+       if (i > 0)
+           break;
+       r = r->next;
+     }
+     /* insert into route list; sorted */
+     if (!(r = malloc(sizeof(*r))))
+     {
+       LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n",
+           inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), add.dest.len,
+           strerror(errno));
+       return 0;
+     }
+     memcpy(r, &add, sizeof(*r));
+     bgp_routes6 = bgp_insert_route6(bgp_routes6, r);
+     /* flag established peers for update */
+     for (i = 0; i < BGP_NUM_PEERS; i++)
+       if (bgp_peers[i].state == Established
+               && bgp_peers[i].mp_handling == HandleIPv6Routes)
+           bgp_peers[i].update_routes6 = 1;
+     LOG(4, 0, 0, "Registered BGP route %s/%d\n",
+       inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), add.dest.len);
+     return 1;
+ }
  /* remove route from list for peers */
  int bgp_del_route(in_addr_t ip, in_addr_t mask)
  {
      return 1;
  }
  
+ /* remove route from list for peers */
+ int bgp_del_route6(struct in6_addr ip, int prefixlen)
+ {
+     struct bgp_route6_list *r = bgp_routes6;
+     struct bgp_route6_list *e = 0;
+     struct bgp_route6_list del;
+     int i;
+     char ipv6addr[INET6_ADDRSTRLEN];
+     memcpy(&del.dest.prefix, &ip.s6_addr, 16);
+     del.dest.len = prefixlen;
+     del.next = 0;
+     /* find entry in routes list and remove */
+     while (r)
+     {
+       i = memcmp(&r->dest, &del.dest, sizeof(r->dest));
+       if (!i)
+       {
+           if (e)
+               e->next = r->next;
+           else
+               bgp_routes6 = r->next;
+           free(r);
+           break;
+       }
+       e = r;
+       if (i > 0)
+           r = 0; /* stop */
+       else
+           r = r->next;
+     }
+     /* not found */
+     if (!r)
+       return 1;
+     /* flag established peers for update */
+     for (i = 0; i < BGP_NUM_PEERS; i++)
+       if (bgp_peers[i].state == Established
+               && bgp_peers[i].mp_handling == HandleIPv6Routes)
+           bgp_peers[i].update_routes6 = 1;
+     LOG(4, 0, 0, "Removed BGP route %s/%d\n",
+       inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), del.dest.len);
+     return 1;
+ }
  /* enable or disable routing */
  void bgp_enable_routing(int enable)
  {
@@@ -627,6 -809,14 +817,14 @@@ int bgp_process(uint32_t events[]
                continue;
        }
  
+       /* process pending IPv6 updates */
+       if (peer->update_routes6
+           && !peer->outbuf->packet.header.len) /* ditto */
+       {
+           if (!bgp_send_update6(peer))
+               continue;
+       }
        /* process timers */
        if (peer->state == Established)
        {
@@@ -670,6 -860,17 +868,17 @@@ static void bgp_free_routes(struct bgp_
      }
  }
  
+ static void bgp_free_routes6(struct bgp_route6_list *routes)
+ {
+     struct bgp_route6_list *tmp;
+     while ((tmp = routes))
+     {
+       routes = tmp->next;
+       free(tmp);
+     }
+ }
  char const *bgp_state_str(enum bgp_state state)
  {
      switch (state)
@@@ -704,7 -905,6 +913,7 @@@ static int bgp_connect(struct bgp_peer 
  {
      static int bgp_port = 0;
      struct sockaddr_in addr;
 +    struct sockaddr_in source_addr;
      struct epoll_event ev;
  
      if (!bgp_port)
      /* set to non-blocking */
      fcntl(peer->sock, F_SETFL, fcntl(peer->sock, F_GETFL, 0) | O_NONBLOCK);
  
 +    /* set source address */
 +    memset(&source_addr, 0, sizeof(source_addr));
 +    source_addr.sin_family = AF_INET;
 +    source_addr.sin_addr.s_addr = peer->source_addr; /* defaults to INADDR_ANY */
 +    if (bind(peer->sock, (struct sockaddr *) &source_addr, sizeof(source_addr)) < 0)
 +    {
 +      LOG(1, 0, 0, "Can't set source address to %s: %s\n",
 +          inet_ntoa(source_addr.sin_addr), strerror(errno));
 +
 +      bgp_set_retry(peer);
 +      return 0;
 +    }
 +
      /* try connect */
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
@@@ -924,6 -1111,12 +1133,12 @@@ static int bgp_handle_input(struct bgp_
            struct bgp_data_open data;
            int hold;
            int i;
+           off_t param_offset, capability_offset;
+           struct bgp_opt_param *param;
+           uint8_t capabilities_len;
+           char *capabilities = NULL;
+           struct bgp_capability *capability;
+           struct bgp_mp_cap_param *mp_cap;
  
            for (i = 0; i < sizeof(p->header.marker); i++)
            {
            if (peer->keepalive * 3 > peer->hold)
                peer->keepalive = peer->hold / 3;
  
+           /* check for optional parameters */
+           /* 2 is for the size of type + len (both uint8_t) */
+           for (param_offset = 0;
+                   param_offset < data.opt_len;
+                   param_offset += 2 + param->len)
+           {
+               param = (struct bgp_opt_param *)((char *)&data.opt_params + param_offset);
+               /* sensible check */
+               if (data.opt_len - param_offset < 2
+                       || param->len > data.opt_len - param_offset - 2)
+               {
+                   LOG(1, 0, 0, "Malformed Optional Parameter list from BGP peer %s\n",
+                       peer->name);
+                   bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_UNSPEC);
+                   return 0;
+               }
+               /* we know only one parameter type */
+               if (param->type != BGP_PARAM_TYPE_CAPABILITY)
+               {
+                   LOG(1, 0, 0, "Unsupported Optional Parameter type %d from BGP peer %s\n",
+                       param->type, peer->name);
+                   bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_UNSUP_PARAM);
+                   return 0;
+               }
+               capabilities_len = param->len;
+               capabilities = (char *)&param->value;
+               /* look for BGP multiprotocol capability */
+               for (capability_offset = 0;
+                       capability_offset < capabilities_len;
+                       capability_offset += 2 + capability->len)
+               {
+                   capability = (struct bgp_capability *)(capabilities + capability_offset);
+                   /* sensible check */
+                   if (capabilities_len - capability_offset < 2
+                           || capability->len > capabilities_len - capability_offset - 2)
+                   {
+                       LOG(1, 0, 0, "Malformed Capabilities list from BGP peer %s\n",
+                           peer->name);
+                       bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_UNSPEC);
+                       return 0;
+                   }
+                   /* we only know one capability code */
+                   if (capability->code != BGP_CAP_CODE_MP
+                           && capability->len != sizeof(struct bgp_mp_cap_param))
+                   {
+                       LOG(4, 0, 0, "Unsupported Capability code %d from BGP peer %s\n",
+                           capability->code, peer->name);
+                       /* we don't terminate, still; we just jump to the next one */
+                       continue;
+                   }
+                   mp_cap = (struct bgp_mp_cap_param *)&capability->value;
+                   /* the only <AFI, SAFI> tuple we support */
+                   if (ntohs(mp_cap->afi) != BGP_MP_AFI_IPv6 && mp_cap->safi != BGP_MP_SAFI_UNICAST)
+                   {
+                       LOG(4, 0, 0, "Unsupported multiprotocol AFI %d and SAFI %d from BGP peer %s\n",
+                           mp_cap->afi, mp_cap->safi, peer->name);
+                       /* we don't terminate, still; we just jump to the next one */
+                       continue;
+                   }
+                   /* yes it can! */
+                   peer->mp_handling = HandleIPv6Routes;
+               }
+           }
+           if (peer->mp_handling != HandleIPv6Routes)
+           {
+               peer->mp_handling = DoesntHandleIPv6Routes;
+               if (config->ipv6_prefix.s6_addr[0])
+                   LOG(1, 0, 0, "Warning: BGP peer %s doesn't handle IPv6 prefixes updates\n",
+                           peer->name);
+           }
            /* next transition requires an exchange of keepalives */
            bgp_send_keepalive(peer);
-           /* FIXME: may need to check for optional params */
        }
  
        break;
            if (notification->error_code == BGP_ERR_CEASE)
            {
                LOG(4, 0, 0, "BGP peer %s sent CEASE\n", peer->name);
 -              bgp_restart(peer);
 +              bgp_set_retry(peer);
                return 0;
            }
  
+           if (notification->error_code == BGP_ERR_OPEN
+                   && notification->error_subcode == BGP_ERR_OPN_UNSUP_PARAM)
+           {
+               LOG(4, 0, 0, "BGP peer %s doesn't support BGP Capabilities\n", peer->name);
+               peer->mp_handling = DoesntHandleIPv6Routes;
+               bgp_set_retry(peer);
+               return 0;
+           }
+           if (notification->error_code == BGP_ERR_OPEN
+                   && notification->error_subcode == BGP_ERR_OPN_UNSUP_CAP)
+           {
+               /* the only capability we advertise is this one, so upon receiving
+                  an "unsupported capability" message, we disable IPv6 routes for
+                  this peer */
+               LOG(4, 0, 0, "BGP peer %s doesn't support IPv6 routes advertisement\n", peer->name);
+               peer->mp_handling = DoesntHandleIPv6Routes;
+               break;
+           }
            /* FIXME: should handle more notifications */
            LOG(4, 0, 0, "BGP peer %s sent unhandled NOTIFICATION %d\n",
                peer->name, (int) notification->error_code);
  static int bgp_send_open(struct bgp_peer *peer)
  {
      struct bgp_data_open data;
+     struct bgp_mp_cap_param mp_ipv6 = { htons(BGP_MP_AFI_IPv6), 0, BGP_MP_SAFI_UNICAST };
+     struct bgp_capability cap_mp_ipv6;
+     struct bgp_opt_param param_cap_mp_ipv6;
      uint16_t len = sizeof(peer->outbuf->packet.header);
  
      memset(peer->outbuf->packet.header.marker, 0xff,
      data.as = htons(our_as);
      data.hold_time = htons(peer->hold);
      data.identifier = my_address;
-     data.opt_len = 0;
  
-     memcpy(peer->outbuf->packet.data, &data, BGP_DATA_OPEN_SIZE);
-     len += BGP_DATA_OPEN_SIZE;
+     /* if we know peer doesn't support MP (mp_handling == DoesntHandleIPv6Routes)
+        then don't add this parameter */
+     if (config->ipv6_prefix.s6_addr[0]
+           && (peer->mp_handling == HandlingUnknown
+               || peer->mp_handling == HandleIPv6Routes))
+     {
+       /* construct the param and capability */
+       cap_mp_ipv6.code = BGP_CAP_CODE_MP;
+       cap_mp_ipv6.len = sizeof(mp_ipv6);
+       memcpy(&cap_mp_ipv6.value, &mp_ipv6, cap_mp_ipv6.len);
+       param_cap_mp_ipv6.type = BGP_PARAM_TYPE_CAPABILITY;
+       param_cap_mp_ipv6.len = 2 + sizeof(mp_ipv6);
+       memcpy(&param_cap_mp_ipv6.value, &cap_mp_ipv6, param_cap_mp_ipv6.len);
+       data.opt_len = 2 + param_cap_mp_ipv6.len;
+       memcpy(&data.opt_params, &param_cap_mp_ipv6, data.opt_len);
+     }
+     else
+       data.opt_len = 0;
+     memcpy(peer->outbuf->packet.data, &data, BGP_DATA_OPEN_SIZE + data.opt_len);
+     len += BGP_DATA_OPEN_SIZE + data.opt_len;
  
      peer->outbuf->packet.header.len = htons(len);
      peer->outbuf->done = 0;
@@@ -1233,6 -1552,179 +1574,179 @@@ static int bgp_send_update(struct bgp_p
      return bgp_write(peer);
  }
  
+ /* send/buffer UPDATE message for IPv6 routes */
+ static int bgp_send_update6(struct bgp_peer *peer)
+ {
+     uint16_t attr_len;
+     uint16_t unreach_len = 0;
+     char *unreach_len_pos;
+     uint8_t reach_len;
+     uint16_t len = sizeof(peer->outbuf->packet.header);
+     struct bgp_route6_list *have = peer->routes6;
+     struct bgp_route6_list *want = peer->routing ? bgp_routes6 : 0;
+     struct bgp_route6_list *e = 0;
+     struct bgp_route6_list *add = 0;
+     int s;
+     char ipv6addr[INET6_ADDRSTRLEN];
+     char *data = (char *) &peer->outbuf->packet.data;
+     /* need leave room for attr_len, bgp_path_attrs and one prefix */
+     char *max = (char *) &peer->outbuf->packet.data
+       + sizeof(peer->outbuf->packet.data)
+       - sizeof(attr_len) - peer->path_attr_len_without_nexthop
+       - BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE - sizeof(struct bgp_ip6_prefix);
+     memset(peer->outbuf->packet.header.marker, 0xff,
+       sizeof(peer->outbuf->packet.header.marker));
+     peer->outbuf->packet.header.type = BGP_MSG_UPDATE;
+     /* insert non-MP unfeasible routes length */
+     memcpy(data, &unreach_len, sizeof(unreach_len));
+     /* skip over it and attr_len too; it will be filled when known */
+     data += sizeof(unreach_len) + sizeof(attr_len);
+     len += sizeof(unreach_len) + sizeof(attr_len);
+     /* copy usual attributes */
+     memcpy(data, peer->path_attrs, peer->path_attr_len_without_nexthop);
+     data += peer->path_attr_len_without_nexthop;
+     attr_len = peer->path_attr_len_without_nexthop;
+     /* copy MP unreachable NLRI heading */
+     memcpy(data, peer->mp_unreach_nlri_partial,
+           BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE);
+     /* remember where to update this attr len */
+     unreach_len_pos = data + 2;
+     data += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
+     attr_len += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
+     peer->update_routes6 = 0; /* tentatively clear */
+     /* find differences */
+     while ((have || want) && data < (max - sizeof(struct bgp_ip6_prefix)))
+     {
+       if (have)
+           s = want
+               ? memcmp(&have->dest, &want->dest, sizeof(have->dest))
+               : -1;
+       else
+           s = 1;
+       if (s < 0) /* found one to delete */
+       {
+           struct bgp_route6_list *tmp = have;
+           have = have->next;
+           s = BGP_IP_PREFIX_SIZE(tmp->dest);
+           memcpy(data, &tmp->dest, s);
+           data += s;
+           unreach_len += s;
+           attr_len += s;
+           LOG(5, 0, 0, "Withdrawing route %s/%d from BGP peer %s\n",
+               inet_ntop(AF_INET6, &tmp->dest.prefix, ipv6addr, INET6_ADDRSTRLEN),
+               tmp->dest.len, peer->name);
+           free(tmp);
+           if (e)
+               e->next = have;
+           else
+               peer->routes6 = have;
+       }
+       else
+       {
+           if (!s) /* same */
+           {
+               e = have; /* stash the last found to relink above */
+               have = have->next;
+               want = want->next;
+           }
+           else if (s > 0) /* addition reqd. */
+           {
+               if (add)
+               {
+                   peer->update_routes6 = 1; /* only one add per packet */
+                   if (!have)
+                       break;
+               }
+               else
+                   add = want;
+               if (want)
+                   want = want->next;
+           }
+       }
+     }
+     if (have || want)
+       peer->update_routes6 = 1; /* more to do */
+     /* anything changed? */
+     if (!(unreach_len || add))
+       return 1;
+     if (unreach_len)
+     {
+       /* go back and insert MP unreach_len */
+       unreach_len += sizeof(struct bgp_attr_mp_unreach_nlri_partial);
+       unreach_len = htons(unreach_len);
+       memcpy(unreach_len_pos, &unreach_len, sizeof(unreach_len));
+     }
+     else
+     {
+       /* we can remove this attribute, then */
+       data -= BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
+       attr_len -= BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
+     }
+     if (add)
+     {
+       if (!(e = malloc(sizeof(*e))))
+       {
+           LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n",
+               inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN),
+               add->dest.len, strerror(errno));
+           return 0;
+       }
+       memcpy(e, add, sizeof(*e));
+       e->next = 0;
+       peer->routes6 = bgp_insert_route6(peer->routes6, e);
+       /* copy MP reachable NLRI heading */
+       memcpy(data, peer->mp_reach_nlri_partial,
+               BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE);
+       /* with proper len */
+       reach_len = BGP_IP_PREFIX_SIZE(add->dest);
+       data[2] = sizeof(struct bgp_attr_mp_reach_nlri_partial) + reach_len;
+       data += BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE;
+       attr_len += BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE;
+       memcpy(data, &add->dest, reach_len);
+       data += reach_len;
+       attr_len += reach_len;
+       LOG(5, 0, 0, "Advertising route %s/%d to BGP peer %s\n",
+           inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN),
+           add->dest.len, peer->name);
+     }
+     /* update len with attributes we added */
+     len += attr_len;
+     /* go back and insert attr_len */
+     attr_len = htons(attr_len);
+     memcpy((char *)&peer->outbuf->packet.data + 2, &attr_len, sizeof(attr_len));
+     peer->outbuf->packet.header.len = htons(len);
+     peer->outbuf->done = 0;
+     return bgp_write(peer);
+ }
  /* send/buffer NOTIFICATION message */
  static int bgp_send_notification(struct bgp_peer *peer, uint8_t code,
      uint8_t subcode)
diff --combined bgp.h
--- 1/bgp.h
--- 2/bgp.h
+++ b/bgp.h
@@@ -43,11 -43,51 +43,51 @@@ struct bgp_data_open 
      char opt_params[sizeof(((struct bgp_packet *)0)->data) - BGP_DATA_OPEN_SIZE]; /* variable */
  } __attribute__ ((packed));
  
+ struct bgp_opt_param {
+     uint8_t type;
+     uint8_t len;
+ #define BGP_MAX_OPT_PARAM_SIZE        256
+     char value[BGP_MAX_OPT_PARAM_SIZE];
+ } __attribute__ ((packed));
+ #define BGP_PARAM_TYPE_CAPABILITY     2
+ struct bgp_capability {
+     uint8_t code;
+     uint8_t len;
+ #define BGP_MAX_CAPABILITY_SIZE       256
+     char value[BGP_MAX_CAPABILITY_SIZE];
+ } __attribute__ ((packed));
+ /* RFC4760 Multiprotocol extension */
+ #define BGP_CAP_CODE_MP       1
+ struct bgp_mp_cap_param {
+     uint16_t afi; /* sa_family_t */
+     uint8_t reserved; /* SHOULD be 0 */
+     uint8_t safi;
+ } __attribute__ ((packed));
+ /* bgp_mp_cap_param.afi */
+ #define BGP_MP_AFI_RESERVED   0
+ #define BGP_MP_AFI_IPv4               1
+ #define BGP_MP_AFI_IPv6               2
+ /* bgp_mp_cap_param.safi */
+ #define BGP_MP_SAFI_UNICAST   1
+ #define BGP_MP_SAFI_MULTICAST 2
+ struct bgp_ip6_prefix {
+     uint8_t len;
+     uint8_t prefix[16]; /* variable */
+ } __attribute__ ((packed));
+ /* end of RFC4760 specific definitions */
  struct bgp_ip_prefix {
      uint8_t len;
      uint32_t prefix; /* variable */
  } __attribute__ ((packed));
  
+ /* works for both IPv4 and IPv6 prefixes */
  #define BGP_IP_PREFIX_SIZE(p) (1 + ((p).len / 8) + ((p).len % 8 != 0))
  
  struct bgp_path_attr {
      } data; /* variable */
  } __attribute__ ((packed));
  
+ struct bgp_attr_mp_reach_nlri_partial {
+     uint16_t afi; /* sa_family_t */
+     uint8_t safi;
+     uint8_t next_hop_len;
+     uint8_t next_hop[16];
+     uint8_t reserved;
+ } __attribute__ ((packed));
+ #define BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE (3 + sizeof(struct bgp_attr_mp_reach_nlri_partial))
+ struct bgp_attr_mp_unreach_nlri_partial {
+     uint16_t afi; /* sa_family_t */
+     uint8_t safi;
+ } __attribute__ ((packed));
+ /* we use it as an extended attribute */
+ #define BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE (4 + sizeof(struct bgp_attr_mp_unreach_nlri_partial))
  /* bgp_path_attr.flags (bitfields) */
  #define BGP_PATH_ATTR_FLAG_OPTIONAL   (1 << 7)
  #define BGP_PATH_ATTR_FLAG_TRANS      (1 << 6)
  #define BGP_PATH_ATTR_CODE_ATOMIC_AGGREGATE   6       /* well-known, discretionary */
  #define BGP_PATH_ATTR_CODE_AGGREGATOR         7       /* optional, transitive */
  #define BGP_PATH_ATTR_CODE_COMMUNITIES                8       /* optional, transitive (RFC1997) */
+ #define BGP_PATH_ATTR_CODE_MP_REACH_NLRI      14      /* optional, non-transitive (RFC4760) */
+ #define BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI    15      /* optional, non-transitive (RFC4760) */
  
  #define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \
-     ? ((p).data.e.len + 1) : (p).data.s.len) + 3)
+     ? ((p).data.e.len + 4) : (p).data.s.len) + 3)
  
  /* well known COMMUNITIES */
  #define BGP_COMMUNITY_NO_EXPORT                       0xffffff01      /* don't advertise outside confederation */
@@@ -101,6 -159,7 +159,7 @@@ struct bgp_data_notification 
  } __attribute__ ((packed));
  
  /* bgp_data_notification.error_code, .error_subcode */
+ #define BGP_ERR_UNSPEC                        0
  #define BGP_ERR_HEADER                        1
  #  define BGP_ERR_HDR_NOT_SYNC                  1
  #  define BGP_ERR_HDR_BAD_LEN           2
  #  define BGP_ERR_OPN_UNSUP_PARAM       4
  #  define BGP_ERR_OPN_AUTH_FAILURE      5
  #  define BGP_ERR_OPN_HOLD_TIME                 6
+ #  define BGP_ERR_OPN_UNSUP_CAP                 7
  #define BGP_ERR_UPDATE                        3
  #  define BGP_ERR_UPD_BAD_ATTR_LIST     1
  #  define BGP_ERR_UPD_UNKN_WK_ATTR      2
@@@ -138,6 -198,11 +198,11 @@@ enum bgp_state 
      Established,                      /* established */
  };
  
+ struct bgp_route6_list {
+     struct bgp_ip6_prefix dest;
+     struct bgp_route6_list *next;
+ };
  struct bgp_route_list {
      struct bgp_ip_prefix dest;
      struct bgp_route_list *next;
@@@ -148,11 -213,16 +213,17 @@@ struct bgp_buf 
      size_t done;                      /* bytes sent/recvd */
  };
  
+ enum bgp_mp_handling {
+     HandleIPv6Routes,
+     DoesntHandleIPv6Routes,
+     HandlingUnknown,
+ };
  /* state */
  struct bgp_peer {
      char name[32];                    /* peer name */
      in_addr_t addr;                   /* peer address */
 +    in_addr_t source_addr;            /* our source address */
      int as;                           /* AS number */
      int sock;
      enum bgp_state state;             /* FSM state */
      int cli_flag;                     /* updates requested from CLI */
      char *path_attrs;                 /* path attrs to send in UPDATE message */
      int path_attr_len;                        /* length of path attrs */
+     int path_attr_len_without_nexthop;        /* length of path attrs  without NEXT_HOP */
      uint32_t events;                  /* events to poll */
      struct event_data edata;          /* poll data */
+     enum bgp_mp_handling mp_handling; /* how it handles IPv6 routes advertisements */
+     int update_routes6;                       /* UPDATE required for IPv6 routes */
+     struct bgp_route6_list *routes6;  /* IPv6 routes known by this peer */
+     char mp_reach_nlri_partial[BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE];
+     char mp_unreach_nlri_partial[BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE];
  };
  
  /* bgp_peer.cli_flag */
@@@ -189,13 -265,15 +266,15 @@@ extern int bgp_configured
  /* actions */
  int bgp_setup(int as);
  int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive,
 -    int hold, int enable);
 +    int hold, struct in_addr update_source, int enable);
  
  void bgp_stop(struct bgp_peer *peer);
  void bgp_halt(struct bgp_peer *peer);
  int bgp_restart(struct bgp_peer *peer);
  int bgp_add_route(in_addr_t ip, in_addr_t mask);
+ int bgp_add_route6(struct in6_addr ip, int prefixlen);
  int bgp_del_route(in_addr_t ip, in_addr_t mask);
+ int bgp_del_route6(struct in6_addr ip, int prefixlen);
  void bgp_enable_routing(int enable);
  int bgp_set_poll(void);
  int bgp_process(uint32_t events[]);
@@@ -7,9 -7,6 +7,9 @@@ set log_file "/var/log/l2tpns
  # Write pid to this file
  set pid_file "/var/run/l2tpns.pid"
  
 +# This host name, if different from the OS one
 +#set hostname "localhost"
 +
  # Shared secret with LAC
  set l2tp_secret "secret"
  
@@@ -56,9 -53,6 +56,9 @@@ set accounting_dir "/var/run/l2tpns/acc
  # Listen address for L2TP
  #set bind_address 1.1.1.1
  
 +# Listen address for CLI
 +#set cli_bind_address 127.0.0.1
 +
  # Send a gratiuitous ARP for bind address
  #set send_garp no
  
  # IPv6 address prefix
  #set ipv6_prefix ::
  
+ # BGP NEXT_HOP path attribute
+ #set nexthop 10.0.1.1
+ #set nexthop6 2001:db8::1
  # Drop/kill sessions
  #load plugin "sessionctl"
  
diff --combined l2tpns.c
+++ b/l2tpns.c
@@@ -69,7 -69,7 +69,7 @@@ int rand_fd = -1;             // Random data sourc
  int cluster_sockfd = -1;      // Intra-cluster communications socket.
  int epollfd = -1;             // event polling
  time_t basetime = 0;          // base clock
 -char hostname[1000] = "";     // us.
 +char hostname[MAXHOSTNAME] = "";      // us.
  static int tunidx;            // ifr_ifindex of tun device
  static int syslog_log = 0;    // are we logging to syslog
  static FILE *log_stream = 0;  // file handle for direct logging (i.e. direct into file, not via syslog).
@@@ -157,9 -157,7 +157,10 @@@ config_descriptt config_values[] = 
        CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT),
        CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT),
        CONFIG("ipv6_prefix", ipv6_prefix, IPv6),
 +      CONFIG("cli_bind_address", cli_bind_address, IPv4),
 +      CONFIG("hostname", hostname, STRING),
 +      CONFIG("nexthop_address", nexthop_address, IPv4),
+       CONFIG("nexthop6_address", nexthop6_address, IPv6),
        { NULL, 0, 0, 0 },
  };
  
@@@ -167,6 -165,8 +168,6 @@@ static char *plugin_functions[] = 
        NULL,
        "plugin_pre_auth",
        "plugin_post_auth",
 -      "plugin_packet_rx",
 -      "plugin_packet_tx",
        "plugin_timer",
        "plugin_new_session",
        "plugin_kill_session",
@@@ -502,7 -502,12 +503,12 @@@ void route6set(sessionidt s, struct in6
                LOG(0, 0, 0, "route6set() error in ioctl: %s\n",
                                strerror(errno));
  
-       // FIXME: need to add BGP routing (RFC2858)
+ #ifdef BGP
+       if (add)
+               bgp_add_route6(ip, prefixlen);
+       else
+               bgp_del_route6(ip, prefixlen);
+ #endif /* BGP */
  
        if (s)
        {
@@@ -3398,36 -3403,35 +3404,36 @@@ static int still_busy(void
        static clockt last_talked = 0;
        static clockt start_busy_wait = 0;
  
 -      if (!config->cluster_iam_master)
 -      {
  #ifdef BGP
 -              static time_t stopped_bgp = 0;
 -              if (bgp_configured)
 +      static time_t stopped_bgp = 0;
 +      if (bgp_configured)
 +      {
 +              if (!stopped_bgp)
                {
 -                      if (!stopped_bgp)
 -                      {
 -                              LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY);
 +                      LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY);
  
 -                              for (i = 0; i < BGP_NUM_PEERS; i++)
 -                                      if (bgp_peers[i].state == Established)
 -                                              bgp_stop(&bgp_peers[i]);
 +                      for (i = 0; i < BGP_NUM_PEERS; i++)
 +                              if (bgp_peers[i].state == Established)
 +                                      bgp_stop(&bgp_peers[i]);
  
 -                              stopped_bgp = time_now;
 +                      stopped_bgp = time_now;
  
 +                      if (!config->cluster_iam_master)
 +                      {
                                // we don't want to become master
                                cluster_send_ping(0);
  
                                return 1;
                        }
 -
 -                      if (time_now < (stopped_bgp + QUIT_DELAY))
 -                              return 1;
                }
 +
 +              if (!config->cluster_iam_master && time_now < (stopped_bgp + QUIT_DELAY))
 +                      return 1;
 +      }
  #endif /* BGP */
  
 +      if (!config->cluster_iam_master)
                return 0;
 -      }
  
        if (main_quit == QUIT_SHUTDOWN)
        {
@@@ -3568,8 -3572,7 +3574,8 @@@ static void mainloop(void
                if (config->neighbour[i].name[0])
                        bgp_start(&bgp_peers[i], config->neighbour[i].name,
                                config->neighbour[i].as, config->neighbour[i].keepalive,
 -                              config->neighbour[i].hold, 0); /* 0 = routing disabled */
 +                              config->neighbour[i].hold, config->neighbour[i].update_source,
 +                              0); /* 0 = routing disabled */
        }
  #endif /* BGP */
  
@@@ -4070,14 -4073,9 +4076,14 @@@ static void initdata(int optdebug, cha
  
        if (!*hostname)
        {
 -              // Grab my hostname unless it's been specified
 -              gethostname(hostname, sizeof(hostname));
 -              stripdomain(hostname);
 +              if (!*config->hostname)
 +              {
 +                      // Grab my hostname unless it's been specified
 +                      gethostname(hostname, sizeof(hostname));
 +                      stripdomain(hostname);
 +              }
 +              else
 +                      strcpy(hostname, config->hostname);
        }
  
        _statistics->start_time = _statistics->last_reset = time(NULL);
diff --combined l2tpns.h
+++ b/l2tpns.h
@@@ -38,7 -38,6 +38,7 @@@
  #define PPPoE_MRU     1492            // maximum PPPoE MRU (rfc2516: 1500 less PPPoE header (6) and PPP protocol ID (2))
  #define MAXETHER      (MAXMTU+18)     // max packet we try sending to tun
  #define MAXTEL                96              // telephone number
 +#define MAXHOSTNAME   256             // hostname
  #define MAXUSER               128             // username
  #define MAXPASS               128             // password
  #define MAXPLUGINS    20              // maximum number of plugins to load
@@@ -713,8 -712,6 +713,8 @@@ typedef struc
  
        int             cluster_master_min_adv;         // Master advertises routes while the number of up to date
                                                        // slaves is less than this value.
 +      in_addr_t       cli_bind_address;               // bind address for CLI
 +      char            hostname[MAXHOSTNAME];  // hostname (overridden by -h on command line)
        // Guest change
        char            guest_user[MAXUSER];            // Guest account username
  
                uint16_t as;
                int keepalive;
                int hold;
 +              struct in_addr update_source;
        } neighbour[BGP_NUM_PEERS];
 +      in_addr_t       nexthop_address;
+       struct in6_addr nexthop6_address;
  #endif
  } configt;