local-address is one of those values that need to be set in some cases but
is not very flexible to use. This diff tries to change this a bit.

It allows to set the local-address for both IPv4 and IPv6 at the same time
and also allows to unset a previously set local-address. For example:

group IBGP {
        local-address 192.0.2.1
        local-address 2001:db8:abcd::1

        neighbor 192.0.2.2 { remote-as $AS }
        neighbor 2001:db8:abcd::2 { remote-as $AS }

        # reset the local-address for whatever reason
        neighbor 192.0.2.3 {
                no local-address
                remote-as $AS
        }
}

As usual setting a local-address on the neighbor will override the group
config. I think for IBGP and multihop sessions this can simplify the
config a fair bit. In my case this will collaps IPv4 and IPv6 specific
groups back together since the only reason they are split is because of
local-address.

What do other bgpd user think?
-- 
:wq Claudio

Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.401
diff -u -p -r1.401 bgpd.h
--- bgpd.h      14 Feb 2020 13:54:31 -0000      1.401
+++ bgpd.h      22 Apr 2020 15:50:46 -0000
@@ -365,7 +365,8 @@ struct capabilities {
 
 struct peer_config {
        struct bgpd_addr         remote_addr;
-       struct bgpd_addr         local_addr;
+       struct bgpd_addr         local_addr_v4;
+       struct bgpd_addr         local_addr_v6;
        struct peer_auth         auth;
        struct capabilities      capabilities;
        char                     group[PEER_DESCR_LEN];
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.405
diff -u -p -r1.405 parse.y
--- parse.y     16 Mar 2020 14:47:30 -0000      1.405
+++ parse.y     23 Apr 2020 07:51:25 -0000
@@ -1260,8 +1260,27 @@ peeropts : REMOTEAS as4number    {
                        free($2);
                }
                | LOCALADDR address     {
-                       memcpy(&curpeer->conf.local_addr, &$2,
-                           sizeof(curpeer->conf.local_addr));
+                       if ($2.aid == AID_INET)
+                               memcpy(&curpeer->conf.local_addr_v4, &$2,
+                                   sizeof(curpeer->conf.local_addr_v4));
+                       else if ($2.aid == AID_INET6)
+                               memcpy(&curpeer->conf.local_addr_v6, &$2,
+                                   sizeof(curpeer->conf.local_addr_v6));
+                       else {
+                               yyerror("Unsupported address family %s for "
+                                   "local-addr", aid2str($2.aid));
+                               YYERROR;
+                       }
+               }
+               | yesno LOCALADDR       {
+                       if ($1) {
+                               yyerror("bad local-address definition");
+                               YYERROR;
+                       }
+                       memset(&curpeer->conf.local_addr_v4, 0,
+                           sizeof(curpeer->conf.local_addr_v4));
+                       memset(&curpeer->conf.local_addr_v6, 0,
+                           sizeof(curpeer->conf.local_addr_v6));
                }
                | MULTIHOP NUMBER       {
                        if ($2 < 2 || $2 > 255) {
@@ -4176,11 +4195,17 @@ str2key(char *s, char *dest, size_t max_
 int
 neighbor_consistent(struct peer *p)
 {
-       /* local-address and peer's address: same address family */
-       if (p->conf.local_addr.aid &&
-           p->conf.local_addr.aid != p->conf.remote_addr.aid) {
-               yyerror("local-address and neighbor address "
-                   "must be of the same address family");
+       struct bgpd_addr *local_addr;
+
+       switch (p->conf.remote_addr.aid) {
+       case AID_INET:
+               local_addr = &p->conf.local_addr_v4;
+               break;
+       case AID_INET6:
+               local_addr = &p->conf.local_addr_v6;
+               break;
+       default:
+               yyerror("Bad address family for remote-addr");
                return (-1);
        }
 
@@ -4189,7 +4214,7 @@ neighbor_consistent(struct peer *p)
            p->conf.auth.method == AUTH_IPSEC_IKE_AH ||
            p->conf.auth.method == AUTH_IPSEC_MANUAL_ESP ||
            p->conf.auth.method == AUTH_IPSEC_MANUAL_AH) &&
-           !p->conf.local_addr.aid) {
+           local_addr->aid == AID_UNSPEC) {
                yyerror("neighbors with any form of IPsec configured "
                    "need local-address to be specified");
                return (-1);
Index: pfkey.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/pfkey.c,v
retrieving revision 1.60
diff -u -p -r1.60 pfkey.c
--- pfkey.c     1 Oct 2019 11:05:30 -0000       1.60
+++ pfkey.c     22 Apr 2020 16:08:33 -0000
@@ -55,6 +55,18 @@ int  pfkey_send(int, uint8_t, uint8_t, ui
        pfkey_send(fd, satype, cmd, dir, from, to, \
            0, 0, 0, NULL, 0, 0, NULL, sport, dport)
 
+static struct bgpd_addr *
+pfkey_localaddr(struct peer *p)
+{
+       switch (p->conf.remote_addr.aid) {
+       case AID_INET:
+               return &p->conf.local_addr_v4;
+       case AID_INET6:
+               return &p->conf.local_addr_v6;
+       }
+       fatalx("Unknown AID in pfkey_localaddr");
+}
+
 int
 pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
     struct bgpd_addr *src, struct bgpd_addr *dst, u_int32_t spi,
@@ -530,12 +542,12 @@ pfkey_md5sig_establish(struct peer *p)
        u_int32_t spi_out = 0;
        u_int32_t spi_in = 0;
 
-       if (pfkey_sa_add(&p->conf.local_addr, &p->conf.remote_addr,
+       if (pfkey_sa_add(pfkey_localaddr(p), &p->conf.remote_addr,
            p->conf.auth.md5key_len, p->conf.auth.md5key,
            &spi_out) == -1)
                goto fail;
 
-       if (pfkey_sa_add(&p->conf.remote_addr, &p->conf.local_addr,
+       if (pfkey_sa_add(&p->conf.remote_addr, pfkey_localaddr(p),
            p->conf.auth.md5key_len, p->conf.auth.md5key,
            &spi_in) == -1)
                goto fail;
@@ -582,6 +594,7 @@ static int
 pfkey_ipsec_establish(struct peer *p)
 {
        uint8_t satype = SADB_SATYPE_ESP;
+       struct bgpd_addr *local_addr = pfkey_localaddr(p);
 
        /* cleanup first, unlike in the TCP MD5 case */
        if (p->auth.established) {
@@ -601,7 +614,7 @@ pfkey_ipsec_establish(struct peer *p)
                satype = p->auth.method == AUTH_IPSEC_MANUAL_ESP ?
                    SADB_SATYPE_ESP : SADB_SATYPE_AH;
                if (pfkey_send(pfkey_fd, satype, SADB_ADD, 0,
-                   &p->conf.local_addr, &p->conf.remote_addr,
+                   local_addr, &p->conf.remote_addr,
                    p->conf.auth.spi_out,
                    p->conf.auth.auth_alg_out,
                    p->conf.auth.auth_keylen_out,
@@ -614,7 +627,7 @@ pfkey_ipsec_establish(struct peer *p)
                if (pfkey_reply(pfkey_fd, NULL) == -1)
                        goto fail_key;
                if (pfkey_send(pfkey_fd, satype, SADB_ADD, 0,
-                   &p->conf.remote_addr, &p->conf.local_addr,
+                   &p->conf.remote_addr, local_addr,
                    p->conf.auth.spi_in,
                    p->conf.auth.auth_alg_in,
                    p->conf.auth.auth_keylen_in,
@@ -632,25 +645,25 @@ pfkey_ipsec_establish(struct peer *p)
        }
 
        if (pfkey_flow(pfkey_fd, satype, SADB_X_ADDFLOW, IPSP_DIRECTION_OUT,
-           &p->conf.local_addr, &p->conf.remote_addr, 0, BGP_PORT) == -1)
+           local_addr, &p->conf.remote_addr, 0, BGP_PORT) == -1)
                goto fail_flow;
        if (pfkey_reply(pfkey_fd, NULL) == -1)
                goto fail_flow;
 
        if (pfkey_flow(pfkey_fd, satype, SADB_X_ADDFLOW, IPSP_DIRECTION_OUT,
-           &p->conf.local_addr, &p->conf.remote_addr, BGP_PORT, 0) == -1)
+           local_addr, &p->conf.remote_addr, BGP_PORT, 0) == -1)
                goto fail_flow;
        if (pfkey_reply(pfkey_fd, NULL) == -1)
                goto fail_flow;
 
        if (pfkey_flow(pfkey_fd, satype, SADB_X_ADDFLOW, IPSP_DIRECTION_IN,
-           &p->conf.remote_addr, &p->conf.local_addr, 0, BGP_PORT) == -1)
+           &p->conf.remote_addr, local_addr, 0, BGP_PORT) == -1)
                goto fail_flow;
        if (pfkey_reply(pfkey_fd, NULL) == -1)
                goto fail_flow;
 
        if (pfkey_flow(pfkey_fd, satype, SADB_X_ADDFLOW, IPSP_DIRECTION_IN,
-           &p->conf.remote_addr, &p->conf.local_addr, BGP_PORT, 0) == -1)
+           &p->conf.remote_addr, local_addr, BGP_PORT, 0) == -1)
                goto fail_flow;
        if (pfkey_reply(pfkey_fd, NULL) == -1)
                goto fail_flow;
@@ -768,7 +781,7 @@ pfkey_establish(struct peer *p)
         * remote_addr cannot change, so no copy, SPI are
         * handled by the method specific functions.
         */
-       memcpy(&p->auth.local_addr, &p->conf.local_addr,
+       memcpy(&p->auth.local_addr, pfkey_localaddr(p),
            sizeof(p->auth.local_addr));
        p->auth.method = p->conf.auth.method;
 
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.141
diff -u -p -r1.141 printconf.c
--- printconf.c 24 Jan 2020 05:44:05 -0000      1.141
+++ printconf.c 22 Apr 2020 16:09:18 -0000
@@ -608,8 +608,12 @@ print_peer(struct peer_config *p, struct
                printf("%s\tmultihop %u\n", c, p->distance);
        if (p->passive)
                printf("%s\tpassive\n", c);
-       if (p->local_addr.aid)
-               printf("%s\tlocal-address %s\n", c, log_addr(&p->local_addr));
+       if (p->local_addr_v4.aid)
+               printf("%s\tlocal-address %s\n", c,
+                  log_addr(&p->local_addr_v4));
+       if (p->local_addr_v6.aid)
+               printf("%s\tlocal-address %s\n", c,
+                  log_addr(&p->local_addr_v6));
        if (p->max_prefix) {
                printf("%s\tmax-prefix %u", c, p->max_prefix);
                if (p->max_prefix_restart)
Index: session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.399
diff -u -p -r1.399 session.c
--- session.c   12 Feb 2020 10:33:56 -0000      1.399
+++ session.c   22 Apr 2020 15:54:22 -0000
@@ -1039,6 +1039,7 @@ int
 session_connect(struct peer *peer)
 {
        struct sockaddr         *sa;
+       struct bgpd_addr        *bind_addr = NULL;
        socklen_t                sa_len;
 
        /*
@@ -1066,8 +1067,16 @@ session_connect(struct peer *peer)
        tcp_md5_set(peer->fd, peer);
        peer->wbuf.fd = peer->fd;
 
-       /* if update source is set we need to bind() */
-       if ((sa = addr2sa(&peer->conf.local_addr, 0, &sa_len)) != NULL) {
+       /* if local-address is set we need to bind() */
+       switch (peer->conf.remote_addr.aid) {
+       case AID_INET:
+               bind_addr = &peer->conf.local_addr_v4;
+               break;
+       case AID_INET6:
+               bind_addr = &peer->conf.local_addr_v6;
+               break;
+       }
+       if (bind_addr && (sa = addr2sa(bind_addr, 0, &sa_len)) != NULL) {
                if (bind(peer->fd, sa, sa_len) == -1) {
                        log_peer_warn(&peer->conf, "session_connect bind");
                        bgp_fsm(peer, EVNT_CON_OPENFAIL);
Index: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.52
diff -u -p -r1.52 util.c
--- util.c      12 Feb 2020 10:33:56 -0000      1.52
+++ util.c      22 Apr 2020 15:54:02 -0000
@@ -844,7 +844,7 @@ addr2sa(struct bgpd_addr *addr, u_int16_
        struct sockaddr_in              *sa_in = (struct sockaddr_in *)&ss;
        struct sockaddr_in6             *sa_in6 = (struct sockaddr_in6 *)&ss;
 
-       if (addr->aid == AID_UNSPEC)
+       if (addr == NULL || addr->aid == AID_UNSPEC)
                return (NULL);
 
        bzero(&ss, sizeof(ss));

Reply via email to