This also finishes the missing bits from our RFC 7552 implementation
because GTSM is mandatory for LDPv6.

To avoid any kind of interoperability problems, I included a few
knobs to enable/disable GTSM on a per-address-family and per-neighbor
basis. Cisco's LDPv6 implementation, for instance, doesn't support GTSM.

Ok?
---
 hello.c     |  10 ++++++
 ldp.h       |   1 +
 ldpd.8      |   8 +++++
 ldpd.c      |  18 ++++++++---
 ldpd.conf.5 |  33 +++++++++++++++++++
 ldpd.h      |  12 ++++++-
 ldpe.h      |   6 ++++
 neighbor.c  |  80 ++++++++++++++++++++++++++++++++++++++++++++++
 packet.c    |   5 +++
 parse.y     |  20 +++++++++++-
 printconf.c |  15 +++++++++
 socket.c    | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 12 files changed, 293 insertions(+), 19 deletions(-)

diff --git a/hello.c b/hello.c
index 72623ca..446499c 100644
--- a/hello.c
+++ b/hello.c
@@ -53,6 +53,8 @@ send_hello(enum hello_type type, struct iface_af *ia, struct 
tnbr *tnbr)
                /* multicast destination address */
                switch (af) {
                case AF_INET:
+                       if (!(leconf->ipv4.flags & F_LDPD_AF_NO_GTSM))
+                               flags |= GTSM_HELLO;
                        dst.v4 = global.mcast_addr_v4;
                        break;
                case AF_INET6:
@@ -363,6 +365,14 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *lm, int 
af,
            (trans_pref == DUAL_STACK_LDPOV6 && af == AF_INET6))))
                nbr = nbr_new(lsr_id, af, ds_tlv, &trans_addr, scope_id);
 
+       /* dynamic LDPv4 GTSM negotiation as per RFC 6720 */
+       if (nbr) {
+               if (flags & GTSM_HELLO)
+                       nbr->flags |= F_NBR_GTSM_NEGOTIATED;
+               else
+                       nbr->flags &= ~F_NBR_GTSM_NEGOTIATED;
+       }
+
        /* update neighbor's configuration sequence number */
        if (nbr && (tlvs_rcvd & F_HELLO_TLV_RCVD_CONF)) {
                if (conf_seqnum > nbr->conf_seqnum &&
diff --git a/ldp.h b/ldp.h
index 7078681..9c2377b 100644
--- a/ldp.h
+++ b/ldp.h
@@ -142,6 +142,7 @@ struct hello_prms_tlv {
 
 #define TARGETED_HELLO         0x8000
 #define REQUEST_TARG_HELLO     0x4000
+#define GTSM_HELLO             0x2000
 
 struct hello_prms_opt4_tlv {
        uint16_t        type;
diff --git a/ldpd.8 b/ldpd.8
index def793a..04414cc 100644
--- a/ldpd.8
+++ b/ldpd.8
@@ -129,6 +129,14 @@ socket used for communication with
 .Re
 .Pp
 .Rs
+.%A C. Pignataro
+.%A R. Asati
+.%D August 2012
+.%R RFC 6720
+.%T The Generalized TTL Security Mechanism (GTSM) for the Label Distribution 
Protocol (LDP)
+.Re
+.Pp
+.Rs
 .%A R. Asati
 .%A C. Pignataro
 .%A K. Raza
diff --git a/ldpd.c b/ldpd.c
index 1c680c0..8de3405 100644
--- a/ldpd.c
+++ b/ldpd.c
@@ -823,10 +823,15 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct 
ldpd_af_conf *xa)
        af_conf->thello_interval = xa->thello_interval;
 
        /* update flags */
-       if (ldpd_process == PROC_LDP_ENGINE &&
-           (af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) &&
-           !(xa->flags & F_LDPD_AF_THELLO_ACCEPT))
-               ldpe_remove_dynamic_tnbrs(af);
+       if (ldpd_process == PROC_LDP_ENGINE) {
+               if ((af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) &&
+                   !(xa->flags & F_LDPD_AF_THELLO_ACCEPT))
+                       ldpe_remove_dynamic_tnbrs(af);
+
+               if ((af_conf->flags & F_LDPD_AF_NO_GTSM) !=
+                   (xa->flags & F_LDPD_AF_NO_GTSM))
+                       ldpe_reset_nbrs(af);
+       }
 
        if ((af_conf->flags & F_LDPD_AF_EXPNULL) !=
            (xa->flags & F_LDPD_AF_EXPNULL))
@@ -988,7 +993,10 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf 
*xconf)
                }
 
                /* update existing nbrps */
-               if (nbrp->keepalive != xn->keepalive ||
+               if (nbrp->flags != xn->flags ||
+                   nbrp->keepalive != xn->keepalive ||
+                   nbrp->gtsm_enabled != xn->gtsm_enabled ||
+                   nbrp->gtsm_hops != xn->gtsm_hops ||
                    nbrp->auth.method != xn->auth.method ||
                    strcmp(nbrp->auth.md5key, xn->auth.md5key) != 0)
                        nbrp_changed = 1;
diff --git a/ldpd.conf.5 b/ldpd.conf.5
index 62f2350..a8d8aec 100644
--- a/ldpd.conf.5
+++ b/ldpd.conf.5
@@ -145,6 +145,28 @@ connected prefixes.
 The default is
 .Ic no .
 .Pp
+.It Xo
+.Ic gtsm-enable
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+If set to
+.Ic yes ,
+.Xr ldpd 8
+will use the GTSM procedures described in RFC 6720 (for the IPv4 
address-family)
+and RFC 7552 (for the IPv6 address-family).
+.Pp
+Since GTSM is mandatory for LDPv6, the only effect of disabling GTSM for the
+IPv6 address-family is that
+.Xr ldpd 8
+will not check the incoming packets' Hop Limit.
+Outgoing packets will still be sent using a Hop Limit of 255 to guarantee
+interoperability.
+.Pp
+If GTSM is enabled, multi-hop neighbors should have either GTSM disabled
+individually or configured with an appropriate gtsm-hops distance.
+The default is
+.Ic yes .
+.Pp
 .It Ic keepalive Ar seconds
 Set the keepalive timeout in seconds.
 The default value is 180; valid range is 3\-65535.
@@ -239,6 +261,17 @@ Neighbor-specific parameters are listed below.
 Set the keepalive timeout in seconds.
 Inherited from the global configuration if not given.
 The default value is 180; valid range is 3\-65535.
+.It Xo
+.Ic gtsm-enable
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+Override the inherited configuration and enable/disable GTSM for this neighbor.
+.It Ic gtsm-hops Ar hops
+Set the maximum number of hops the neighbor may be away.
+When GTSM is enabled for this neighbor, incoming packets are required to have
+a TTL/Hop Limit of 256 minus this value, ensuring they have not passed
+through more than the expected number of hops.
+The default value is 1; valid range is 1\-255.
 .It Ic password Ar secret
 Enable TCP MD5 signatures per RFC 5036.
 .El
diff --git a/ldpd.h b/ldpd.h
index c1f73c4..8ee7404 100644
--- a/ldpd.h
+++ b/ldpd.h
@@ -288,6 +288,8 @@ struct nbr_params {
        LIST_ENTRY(nbr_params)   entry;
        struct in_addr           lsr_id;
        uint16_t                 keepalive;
+       int                      gtsm_enabled;
+       uint8_t                  gtsm_hops;
        struct {
                enum auth_method         method;
                char                     md5key[TCP_MD5_KEY_LEN];
@@ -296,6 +298,8 @@ struct nbr_params {
        uint8_t                  flags;
 };
 #define F_NBRP_KEEPALIVE        0x01
+#define F_NBRP_GTSM             0x02
+#define F_NBRP_GTSM_HOPS        0x04
 
 struct l2vpn_if {
        LIST_ENTRY(l2vpn_if)     entry;
@@ -368,6 +372,7 @@ struct ldpd_af_conf {
 #define        F_LDPD_AF_ENABLED       0x0001
 #define        F_LDPD_AF_THELLO_ACCEPT 0x0002
 #define        F_LDPD_AF_EXPNULL       0x0004
+#define        F_LDPD_AF_NO_GTSM       0x0008
 
 struct ldpd_conf {
        struct in_addr           rtr_id;
@@ -574,13 +579,18 @@ int                ldp_create_socket(int, enum 
socket_type);
 void            sock_set_recvbuf(int);
 int             sock_set_reuse(int, int);
 int             sock_set_bindany(int, int);
-int             sock_set_ipv4_mcast_ttl(int, uint8_t);
 int             sock_set_ipv4_tos(int, int);
 int             sock_set_ipv4_recvif(int, int);
+int             sock_set_ipv4_minttl(int, int);
+int             sock_set_ipv4_ucast_ttl(int fd, int);
+int             sock_set_ipv4_mcast_ttl(int, uint8_t);
 int             sock_set_ipv4_mcast(struct iface *);
 int             sock_set_ipv4_mcast_loop(int);
 int             sock_set_ipv6_dscp(int, int);
 int             sock_set_ipv6_pktinfo(int, int);
+int             sock_set_ipv6_minhopcount(int, int);
+int             sock_set_ipv6_ucast_hops(int, int);
+int             sock_set_ipv6_mcast_hops(int, int);
 int             sock_set_ipv6_mcast(struct iface *);
 int             sock_set_ipv6_mcast_loop(int);
 
diff --git a/ldpe.h b/ldpe.h
index f817ce0..6bb41a7 100644
--- a/ldpe.h
+++ b/ldpe.h
@@ -101,7 +101,10 @@ struct nbr {
                enum auth_method        method;
                char                    md5key[TCP_MD5_KEY_LEN];
        } auth;
+       int                      flags;
 };
+#define F_NBR_GTSM_NEGOTIATED   0x01
+
 RB_HEAD(nbr_id_head, nbr);
 RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare)
 RB_HEAD(nbr_addr_head, nbr);
@@ -236,6 +239,9 @@ void                         nbr_stop_idtimer(struct nbr *);
 int                     nbr_pending_idtimer(struct nbr *);
 int                     nbr_pending_connect(struct nbr *);
 int                     nbr_establish_connection(struct nbr *);
+int                     nbr_gtsm_enabled(struct nbr *, struct nbr_params *);
+int                     nbr_gtsm_setup(int, int, struct nbr_params *);
+int                     nbr_gtsm_check(int, struct nbr *, struct nbr_params *);
 struct nbr_params      *nbr_params_new(struct in_addr);
 struct nbr_params      *nbr_params_find(struct ldpd_conf *, struct in_addr);
 uint16_t                nbr_get_keepalive(int, struct in_addr);
diff --git a/neighbor.c b/neighbor.c
index 84437a0..00ccb17 100644
--- a/neighbor.c
+++ b/neighbor.c
@@ -608,6 +608,11 @@ nbr_establish_connection(struct nbr *nbr)
                return (-1);
        }
 
+       if (nbr_gtsm_check(nbr->fd, nbr, nbrp)) {
+               close(nbr->fd);
+               return (-1);
+       }
+
        /*
         * Send an extra hello to guarantee that the remote peer has formed
         * an adjacency as well.
@@ -636,6 +641,81 @@ nbr_establish_connection(struct nbr *nbr)
        return (0);
 }
 
+int
+nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp)
+{
+       /*
+        * RFC 6720 - Section 3:
+        * "This document allows for the implementation to provide an option to
+        * statically (e.g., via configuration) and/or dynamically override the
+        * default behavior and enable/disable GTSM on a per-peer basis".
+        */
+       if (nbrp && (nbrp->flags & F_NBRP_GTSM))
+               return (nbrp->gtsm_enabled);
+
+       if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM)
+               return (0);
+
+       /* By default, GTSM support has to be negotiated for LDPv4 */
+       if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED))
+               return (0);
+
+       return (1);
+}
+
+int
+nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp)
+{
+       int      ttl = 255;
+
+       if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS))
+               ttl = 256 - nbrp->gtsm_hops;
+
+       switch (af) {
+       case AF_INET:
+               if (sock_set_ipv4_minttl(fd, ttl) == -1)
+                       return (-1);
+               ttl = 255;
+               if (sock_set_ipv4_ucast_ttl(fd, ttl) == -1)
+                       return (-1);
+               break;
+       case AF_INET6:
+               if (sock_set_ipv6_minhopcount(fd, ttl) == -1)
+                       return (-1);
+               ttl = 255;
+               if (sock_set_ipv6_ucast_hops(fd, ttl) == -1)
+                       return (-1);
+               break;
+       default:
+               fatalx("nbr_gtsm_setup: unknown af");
+       }
+
+       return (0);
+}
+
+int
+nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp)
+{
+       if (!nbr_gtsm_enabled(nbr, nbrp)) {
+               /*
+                * For LDPv6, send packets with a Hop Limit of 255 even
+                * when GSTM is disabled to guarantee interoperability.
+                */
+               if (nbr->af == AF_INET6)
+                       /* ignore any possible error */
+                       sock_set_ipv6_ucast_hops(fd, 255);
+               return (0);
+       }
+
+       if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) {
+               log_warnx("%s: error enabling GTSM for lsr-id %s", __func__,
+                   inet_ntoa(nbr->id));
+               return (-1);
+       }
+
+       return (0);
+}
+
 static int
 nbr_act_session_operational(struct nbr *nbr)
 {
diff --git a/packet.c b/packet.c
index 8e98556..55b54bb 100644
--- a/packet.c
+++ b/packet.c
@@ -387,6 +387,11 @@ session_accept_nbr(struct nbr *nbr, int fd)
        socklen_t                len;
 
        nbrp = nbr_params_find(leconf, nbr->id);
+       if (nbr_gtsm_check(fd, nbr, nbrp)) {
+               close(fd);
+               return;
+       }
+
        if (nbrp && nbrp->auth.method == AUTH_MD5SIG) {
                if (sysdep.no_pfkey || sysdep.no_md5sig) {
                        log_warnx("md5sig configured but not available");
diff --git a/parse.y b/parse.y
index 1db77c3..ebceddc 100644
--- a/parse.y
+++ b/parse.y
@@ -136,7 +136,7 @@ static int                   pushback_index;
 %token INTERFACE TNEIGHBOR ROUTERID FIBUPDATE EXPNULL
 %token LHELLOHOLDTIME LHELLOINTERVAL
 %token THELLOHOLDTIME THELLOINTERVAL
-%token THELLOACCEPT AF IPV4 IPV6
+%token THELLOACCEPT AF IPV4 IPV6 GTSMENABLE GTSMHOPS
 %token KEEPALIVE TRANSADDRESS TRANSPREFERENCE DSCISCOINTEROP
 %token NEIGHBOR PASSWORD
 %token L2VPN TYPE VPLS PWTYPE MTU BRIDGE
@@ -324,6 +324,10 @@ afoptsl            :  TRANSADDRESS STRING {
                                YYERROR;
                        }
                }
+               | GTSMENABLE yesno {
+                       if ($2 == 0)
+                               defs->afflags |= F_LDPD_AF_NO_GTSM;
+               }
                | af_defaults
                | iface_defaults
                | tnbr_defaults
@@ -413,6 +417,18 @@ nbr_opts   : KEEPALIVE NUMBER {
                        nbrp->auth.method = AUTH_MD5SIG;
                        free($2);
                }
+               | GTSMENABLE yesno {
+                       nbrp->flags |= F_NBRP_GTSM;
+                       nbrp->gtsm_enabled = $2;
+               }
+               | GTSMHOPS NUMBER {
+                       if ($2 < 1 || $2 > 255) {
+                               yyerror("invalid number of hops %lld", $2);
+                               YYERROR;
+                       }
+                       nbrp->gtsm_hops = $2;
+                       nbrp->flags |= F_NBRP_GTSM_HOPS;
+               }
                ;
 
 pw_defaults    : STATUSTLV yesno {
@@ -807,6 +823,8 @@ lookup(char *s)
                {"ethernet-tagged",             ETHERNETTAGGED},
                {"explicit-null",               EXPNULL},
                {"fib-update",                  FIBUPDATE},
+               {"gtsm-enable",                 GTSMENABLE},
+               {"gtsm-hops",                   GTSMHOPS},
                {"include",                     INCLUDE},
                {"interface",                   INTERFACE},
                {"ipv4",                        IPV4},
diff --git a/printconf.c b/printconf.c
index c3757da..df40c1e 100644
--- a/printconf.c
+++ b/printconf.c
@@ -74,6 +74,11 @@ print_af(int af, struct ldpd_conf *conf, struct ldpd_af_conf 
*af_conf)
        else
                printf("\texplicit-null no\n");
 
+       if (af_conf->flags & F_LDPD_AF_NO_GTSM)
+               printf("\tgtsm-enable no\n");
+       else
+               printf("\tgtsm-enable yes\n");
+
        printf("\tkeepalive %u\n", af_conf->keepalive);
        printf("\ttransport-address %s\n", log_addr(af, &af_conf->trans_addr));
 
@@ -116,6 +121,16 @@ print_nbrp(struct nbr_params *nbrp)
        if (nbrp->flags & F_NBRP_KEEPALIVE)
                printf("\tkeepalive %u\n", nbrp->keepalive);
 
+       if (nbrp->flags & F_NBRP_GTSM) {
+               if (nbrp->gtsm_enabled)
+                       printf("\tgtsm-enable yes\n");
+               else
+                       printf("\tgtsm-enable no\n");
+       }
+
+       if (nbrp->flags & F_NBRP_GTSM_HOPS)
+               printf("\tgtsm-hops %u\n", nbrp->gtsm_hops);
+
        if (nbrp->auth.method == AUTH_MD5SIG)
                printf("\tpassword XXXXXX\n");
 
diff --git a/socket.c b/socket.c
index f5c729e..711e31d 100644
--- a/socket.c
+++ b/socket.c
@@ -112,6 +112,12 @@ ldp_create_socket(int af, enum socket_type type)
                                return (-1);
                        }
                }
+               if (type == LDP_SOCKET_SESSION) {
+                       if (sock_set_ipv4_ucast_ttl(fd, 255) == -1) {
+                               close(fd);
+                               return (-1);
+                       }
+               }
                break;
        case AF_INET6:
                if (sock_set_ipv6_dscp(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
@@ -123,6 +129,16 @@ ldp_create_socket(int af, enum socket_type type)
                                close(fd);
                                return (-1);
                        }
+                       if (sock_set_ipv6_mcast_hops(fd, 255) == -1) {
+                               close(fd);
+                               return (-1);
+                       }
+                       if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) {
+                               if (sock_set_ipv6_minhopcount(fd, 255) == -1) {
+                                       close(fd);
+                                       return (-1);
+                               }
+                       }
                }
                if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
                        if (sock_set_ipv6_pktinfo(fd, 1) == -1) {
@@ -130,6 +146,12 @@ ldp_create_socket(int af, enum socket_type type)
                                return (-1);
                        }
                }
+               if (type == LDP_SOCKET_SESSION) {
+                       if (sock_set_ipv6_ucast_hops(fd, 255) == -1) {
+                               close(fd);
+                               return (-1);
+                       }
+               }
                break;
        }
        switch (type) {
@@ -194,12 +216,10 @@ sock_set_bindany(int fd, int enable)
 }
 
 int
-sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
+sock_set_ipv4_tos(int fd, int tos)
 {
-       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
-           (char *)&ttl, sizeof(ttl)) < 0) {
-               log_warn("%s: error setting IP_MULTICAST_TTL to %d",
-                   __func__, ttl);
+       if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
+               log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos);
                return (-1);
        }
 
@@ -207,10 +227,21 @@ sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
 }
 
 int
-sock_set_ipv4_tos(int fd, int tos)
+sock_set_ipv4_recvif(int fd, int enable)
 {
-       if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
-               log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos);
+       if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
+           sizeof(enable)) < 0) {
+               log_warn("%s: error setting IP_RECVIF", __func__);
+               return (-1);
+       }
+       return (0);
+}
+
+int
+sock_set_ipv4_minttl(int fd, int ttl)
+{
+       if (setsockopt(fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) {
+               log_warn("%s: error setting IP_MINTTL", __func__);
                return (-1);
        }
 
@@ -218,13 +249,26 @@ sock_set_ipv4_tos(int fd, int tos)
 }
 
 int
-sock_set_ipv4_recvif(int fd, int enable)
+sock_set_ipv4_ucast_ttl(int fd, int ttl)
 {
-       if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
-           sizeof(enable)) < 0) {
-               log_warn("%s: error setting IP_RECVIF", __func__);
+       if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
+               log_warn("%s: error setting IP_TTL", __func__);
                return (-1);
        }
+
+       return (0);
+}
+
+int
+sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
+{
+       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+           (char *)&ttl, sizeof(ttl)) < 0) {
+               log_warn("%s: error setting IP_MULTICAST_TTL to %d",
+                   __func__, ttl);
+               return (-1);
+       }
+
        return (0);
 }
 
@@ -284,6 +328,42 @@ sock_set_ipv6_pktinfo(int fd, int enable)
 }
 
 int
+sock_set_ipv6_minhopcount(int fd, int hoplimit)
+{
+       if (setsockopt(fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
+           &hoplimit, sizeof(hoplimit)) < 0) {
+               log_warn("%s: error setting IPV6_MINHOPCOUNT", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+sock_set_ipv6_ucast_hops(int fd, int hoplimit)
+{
+       if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+           &hoplimit, sizeof(hoplimit)) < 0) {
+               log_warn("%s: error setting IPV6_UNICAST_HOPS", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+sock_set_ipv6_mcast_hops(int fd, int hoplimit)
+{
+       if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+           &hoplimit, sizeof(hoplimit)) < 0) {
+               log_warn("%s: error setting IPV6_MULTICAST_HOPS", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
 sock_set_ipv6_mcast(struct iface *iface)
 {
        if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
-- 
1.9.1

Reply via email to