diff --git a/doc/bird.sgml b/doc/bird.sgml index 7277b2b9..aa8a53ec 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -470,7 +470,7 @@ to zero to disable it. An empty is equivalent to import keep filtered + import keep filtered Usually, if an import filter rejects a route, the route is forgotten. When this option is active, these routes are kept in the routing table, but they are hidden and not @@ -1966,6 +1966,9 @@ protocol ospf <name> { ptp netmask <switch>; check link <switch>; ecmp weight <num>; + ttl security [<switch>; | tx only] + tx class|dscp <num>; + tx priority <num>; authentication [none|simple|cryptographic]; password "<text>"; password "<text>" { @@ -2236,6 +2239,20 @@ protocol ospf <name> { prefix) is propagated. It is possible that some hardware drivers or platforms do not implement this feature. Default value is no. + ttl security [ + TTL security is a feature that protects routing protocols + from remote spoofed packets by using TTL 255 instead of TTL 1 + for protocol packets destined to neighbors. Because TTL is + decremented when packets are forwarded, it is non-trivial to + spoof packets with TTL 255 from remote locations. Note that + this option would interfere with OSPF virtual links. + + If this option is enabled, the router will send OSPF packets + with TTL 255 and drop received packets with TTL less than + 255. If this option si set to tx class|dscp|priority These options specify the ToS/DiffServ/Traffic class/Priority of the outgoing OSPF packets. See tx_tos = $2; } | TX PRIORITY expr { OSPF_PATT->tx_priority = $3; } + | TTL SECURITY bool { OSPF_PATT->ttl_security = $3; } + | TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; } | password_list { ospf_check_auth(); } ; diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 7164a4a0..448b08d1 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -88,7 +88,7 @@ ospf_sk_open(struct ospf_iface *ifa) sk->rbsize = rxbufsize(ifa); sk->tbsize = rxbufsize(ifa); sk->data = (void *) ifa; - sk->flags = SKF_LADDR_RX; + sk->flags = SKF_LADDR_RX | (ifa->check_ttl ? SKF_TTL_RX : 0); if (sk_open(sk) != 0) goto err; @@ -134,7 +134,7 @@ ospf_sk_open(struct ospf_iface *ifa) { ifa->all_routers = ospf_is_v2(po) ? IP4_OSPF_ALL_ROUTERS : IP6_OSPF_ALL_ROUTERS; ifa->des_routers = ospf_is_v2(po) ? IP4_OSPF_DES_ROUTERS : IP6_OSPF_DES_ROUTERS; - sk->ttl = 1; /* Hack, this will affect just the multicast packets */ + sk->ttl = ifa->cf->ttl_security ? 255 : 1; if (sk_setup_multicast(sk) < 0) goto err; @@ -535,6 +535,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i ifa->rxbuf = ip->rxbuf; ifa->check_link = ip->check_link; ifa->ecmp_weight = ip->ecmp_weight; + ifa->check_ttl = (ip->ttl_security == 1); ifa->autype = ip->autype; ifa->passwords = ip->passwords; ifa->instance_id = ip->instance_id; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index d6b97ebf..55331697 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -248,6 +248,7 @@ struct ospf_iface u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ u8 ptp_netmask; /* Send real netmask for P2P */ + u8 check_ttl; /* Check incoming packets for TTL 255 */ }; struct ospf_md5 @@ -754,7 +755,8 @@ struct ospf_iface_patt u8 check_link; u8 ecmp_weight; u8 real_bcast; /* Not really used in OSPFv3 */ - u8 ptp_netmask; /* bool but 2 for unspecified */ + u8 ptp_netmask; /* bool + 2 for unspecified */ + u8 ttl_security; /* bool + 2 for TX only */ u8 instance_id; list *passwords; }; diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index d22a2713..c673c8b2 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -311,6 +311,12 @@ ospf_rx_hook(sock *sk, int size) return 1; } + if (ifa->check_ttl && (sk->ttl < 255)) + { + log(L_ERR "%s%I - TTL %d (< 255)", mesg, sk->faddr, sk->ttl); + return 1; + } + if ((unsigned) size < sizeof(struct ospf_packet)) { log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size); diff --git a/proto/rip/config.Y b/proto/rip/config.Y index 4b57a9bf..57e291cb 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -22,12 +22,15 @@ CF_DEFINES #define RIP_CFG ((struct rip_proto_config *) this_proto) #define RIP_IPATT ((struct rip_patt *) this_ipatt) +static inline int rip_cfg_is_old(void) { return RIP_CFG->c.protocol == &proto_rip; } +static inline int rip_cfg_is_ng(void) { return RIP_CFG->c.protocol == &proto_ripng; } + CF_DECLS CF_KEYWORDS(RIP, RIPNG, INFINITY, METRIC, PORT, PERIOD, GARBAGE, TIMEOUT, MODE, BROADCAST, MULTICAST, QUIET, NOLISTEN, VERSION1, - AUTHENTICATION, NONE, PLAINTEXT, MD5, - HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, + AUTHENTICATION, NONE, PLAINTEXT, MD5, TTL, SECURITY, + HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, ONLY, RIP_METRIC, RIP_TAG) %type rip_mode rip_auth @@ -87,6 +90,8 @@ rip_iface_item: | MODE rip_mode { RIP_IPATT->mode |= $2; } | TX tos { RIP_IPATT->tx_tos = $2; } | TX PRIORITY expr { RIP_IPATT->tx_priority = $3; } + | TTL SECURITY bool { RIP_IPATT->ttl_security = $3; } + | TTL SECURITY TX ONLY { RIP_IPATT->ttl_security = 2; } ; rip_iface_opts: @@ -107,6 +112,7 @@ rip_iface_init: RIP_IPATT->metric = 1; RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL; RIP_IPATT->tx_priority = sk_priority_control; + RIP_IPATT->ttl_security = rip_cfg_is_ng() ? 1 : 0; } ; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index f59487dd..40b9789f 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -481,6 +481,14 @@ rip_rx(sock *s, int size) iface = i->iface; + if (i->check_ttl && (s->ttl < 255)) + { + log( L_REMOTE "%s: Discarding packet with TTL %d (< 255) from %I on %s", + p->name, s->ttl, s->faddr, i->iface->name); + return 1; + } + + CHK_MAGIC; DBG( "RIP: message came: %d bytes from %I via %s\n", size, s->faddr, iface ? iface->name : "(dummy)" ); size -= sizeof( struct rip_packet_heading ); @@ -687,6 +695,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ rif->mode = PATT->mode; rif->metric = PATT->metric; rif->multicast = (!(PATT->mode & IM_BROADCAST)) && (flags & IF_MULTICAST); + rif->check_ttl = (PATT->ttl_security == 1); } /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */ @@ -708,10 +717,10 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ rif->sock->flags = rip_is_old(p) ? SKF_V4ONLY : SKF_V6ONLY; if (new) { - rif->sock->ttl = 1; rif->sock->tos = PATT->tx_tos; rif->sock->priority = PATT->tx_priority; - rif->sock->flags |= SKF_LADDR_RX; + rif->sock->ttl = PATT->ttl_security ? 255 : 1; + rif->sock->flags |= SKF_LADDR_RX | (rif->check_ttl ? SKF_TTL_RX : 0); } if (new) { diff --git a/proto/rip/rip.h b/proto/rip/rip.h index d5470f4a..4f3aa5fb 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -109,6 +109,7 @@ struct rip_interface { struct rip_connection *busy; int metric; /* You don't want to put struct rip_patt *patt here -- think about reconfigure */ int mode; + int check_ttl; /* Check incoming packets for TTL 255 */ int triggered; struct object_lock *lock; int multicast; @@ -125,6 +126,7 @@ struct rip_patt { #define IM_VERSION1 16 int tx_tos; int tx_priority; + int ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ }; struct rip_proto_config { diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index 3506f56f..e33bc9c6 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -81,43 +81,57 @@ sk_leave_group4(sock *s, ip_addr maddr) /* BSD RX/TX packet info handling for IPv4 */ /* it uses IP_RECVDSTADDR / IP_RECVIF socket options instead of IP_PKTINFO */ -#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))) -#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_addr)) +#define CMSG_SPACE_PKTINFO4 (CMSG_SPACE(sizeof(struct in_addr)) + \ + CMSG_SPACE(sizeof(struct sockaddr_dl))) +#define CMSG_SPACE_PKTINFO6 CMSG_SPACE(sizeof(struct in6_pktinfo)) -static char * -sk_request_pktinfo4(sock *s) +#define CMSG_RX_SPACE (MAX(CMSG_SPACE_PKTINFO4,CMSG_SPACE_PKTINFO6) + CMSG_SPACE(sizeof(int))) +#define CMSG_TX_SPACE MAX(CMSG_SPACE_PKTINFO4,CMSG_SPACE_PKTINFO6) + + +static inline char * +sk_request_cmsg4_pktinfo(sock *s) { int ok = 1; - if (s->flags & SKF_LADDR_RX) - { - if (setsockopt(s->fd, IPPROTO_IP, IP_RECVDSTADDR, &ok, sizeof(ok)) < 0) - return "IP_RECVDSTADDR"; - if (setsockopt(s->fd, IPPROTO_IP, IP_RECVIF, &ok, sizeof(ok)) < 0) - return "IP_RECVIF"; - } + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVDSTADDR, &ok, sizeof(ok)) < 0) + return "IP_RECVDSTADDR"; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVIF, &ok, sizeof(ok)) < 0) + return "IP_RECVIF"; return NULL; } -static void -sk_process_rx_cmsg4(sock *s, struct cmsghdr *cm) +static inline void +sk_process_cmsg4_pktinfo(sock *s, struct cmsghdr *cm) { - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVDSTADDR) - { - struct in_addr *ra = (struct in_addr *) CMSG_DATA(cm); - s->laddr = ipa_get_in4(ra); - } + if ((cm->cmsg_type == IP_RECVDSTADDR) && (s->flags & SKF_LADDR_RX)) + s->laddr = ipa_get_in4((struct in_addr *) CMSG_DATA(cm)); - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVIF) - { - struct sockaddr_dl *ri = (struct sockaddr_dl *) CMSG_DATA(cm); - s->lifindex = ri->sdl_index; - } - - // log(L_WARN "RX %I %d", s->laddr, s->lifindex); + if ((cm->cmsg_type == IP_RECVIF) && (s->flags & SKF_LADDR_RX)) + s->lifindex = ((struct sockaddr_dl *) CMSG_DATA(cm))->sdl_index; } +static inline char * +sk_request_cmsg4_ttl(sock *s) +{ + int ok = 1; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0) + return "IP_RECVTTL"; + + return NULL; +} + +static inline void +sk_process_cmsg4_ttl(sock *s, struct cmsghdr *cm) +{ + if ((cm->cmsg_type == IP_RECVTTL) && (s->flags & SKF_TTL_RX)) + s->ttl = * (unsigned char *) CMSG_DATA(cm); +} + + /* Unfortunately, IP_SENDSRCADDR does not work for raw IP sockets on BSD kernels */ /* static void diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 13f3713e..8c52d868 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -157,34 +157,52 @@ sk_set_md5_auth_int(sock *s, struct sockaddr *sa, int sa_len, char *passwd) /* RX/TX packet info handling for IPv4 */ /* Mostly similar to standardized IPv6 code */ -#define CMSG_SPACE_PKTINFO4 CMSG_SPACE(sizeof(struct in_pktinfo)) -#define CMSG_SPACE_PKTINFO6 CMSG_SPACE(sizeof(struct in6_pktinfo)) - -#define CMSG_RX_SPACE MAX(CMSG_SPACE_PKTINFO4,CMSG_SPACE_PKTINFO6) -#define CMSG_TX_SPACE MAX(CMSG_SPACE_PKTINFO4,CMSG_SPACE_PKTINFO6) +#define CMSG4_SPACE_PKTINFO CMSG_SPACE(sizeof(struct in_pktinfo)) static inline char * -sk_request_pktinfo4(sock *s) +sk_request_cmsg4_pktinfo(sock *s) { int ok = 1; - if (s->flags & SKF_LADDR_RX) - if (setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0) - return "IP_PKTINFO"; + + if (setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0) + return "IP_PKTINFO"; return NULL; } static inline void -sk_process_rx_cmsg4(sock *s, struct cmsghdr *cm) +sk_process_cmsg4_pktinfo(sock *s, struct cmsghdr *cm) { - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO) - { - struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cm); - s->laddr = ipa_get_in4(&pi->ipi_addr); - s->lifindex = pi->ipi_ifindex; - } + if ((cm->cmsg_type == IP_PKTINFO) && (s->flags & SKF_LADDR_RX)) + { + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cm); + s->laddr = ipa_get_in4(&pi->ipi_addr); + s->lifindex = pi->ipi_ifindex; + } } + +#define CMSG4_SPACE_TTL CMSG_SPACE(sizeof(int)) + +static inline char * +sk_request_cmsg4_ttl(sock *s) +{ + int ok = 1; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0) + return "IP_RECVTTL"; + + return NULL; +} + +static inline void +sk_process_cmsg4_ttl(sock *s, struct cmsghdr *cm) +{ + if ((cm->cmsg_type == IP_TTL) && (s->flags & SKF_TTL_RX)) + s->ttl = * (int *) CMSG_DATA(cm); +} + + /* static void sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 93fe06e9..85b16b09 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -700,42 +700,96 @@ sk_insert(sock *s) #ifndef IPV6_RECVPKTINFO #define IPV6_RECVPKTINFO IPV6_PKTINFO #endif +/* + * Same goes for IPV6_HOPLIMIT -> IPV6_RECVHOPLIMIT. + */ +#ifndef IPV6_RECVHOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif + + +#define CMSG6_SPACE_PKTINFO CMSG_SPACE(sizeof(struct in6_pktinfo)) static inline char * -sk_request_pktinfo6(sock *s) +sk_request_cmsg6_pktinfo(sock *s) { int ok = 1; - if (s->flags & SKF_LADDR_RX) - if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0) - return "IPV6_RECVPKTINFO"; + + if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0) + return "IPV6_RECVPKTINFO"; return NULL; } +static inline void +sk_process_cmsg6_pktinfo(sock *s, struct cmsghdr *cm) +{ + if ((cm->cmsg_type == IPV6_PKTINFO) && (s->flags & SKF_LADDR_RX)) + { + struct in6_pktinfo *pi = (struct in6_pktinfo *) CMSG_DATA(cm); + s->laddr = ipa_get_in6(&pi->ipi6_addr); + s->lifindex = pi->ipi6_ifindex; + } +} + + +#define CMSG6_SPACE_TTL CMSG_SPACE(sizeof(int)) + +static inline char * +sk_request_cmsg6_ttl(sock *s) +{ + int ok = 1; + + if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &ok, sizeof(ok)) < 0) + return "IPV6_RECVHOPLIMIT"; + + return NULL; +} + +static inline void +sk_process_cmsg6_ttl(sock *s, struct cmsghdr *cm) +{ + if ((cm->cmsg_type == IPV6_HOPLIMIT) && (s->flags & SKF_TTL_RX)) + s->ttl = * (int *) CMSG_DATA(cm); +} + + +#define CMSG_RX_SPACE MAX(CMSG4_SPACE_PKTINFO+CMSG4_SPACE_TTL, \ + CMSG6_SPACE_PKTINFO+CMSG6_SPACE_TTL) +#define CMSG_TX_SPACE MAX(CMSG4_SPACE_PKTINFO,CMSG6_SPACE_PKTINFO) + + static void -sk_process_rx_cmsgs(sock *s, struct msghdr *msg) +sk_process_cmsgs(sock *s, struct msghdr *msg) { struct cmsghdr *cm; - if (!(s->flags & SKF_LADDR_RX)) - return; + if (s->flags & SKF_LADDR_RX) + { + s->laddr = IPA_NONE; + s->lifindex = 0; + } - s->laddr = IPA_NONE; - s->lifindex = 0; + if (s->flags & SKF_TTL_RX) + s->ttl = -1; for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if ((cm->cmsg_level == IPPROTO_IP) && sk_is_ipv4(s)) { - if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO) - { - struct in6_pktinfo *pi = (struct in6_pktinfo *) CMSG_DATA(cm); - s->laddr = ipa_get_in6(&pi->ipi6_addr); - s->lifindex = pi->ipi6_ifindex; - } - - sk_process_rx_cmsg4(s, cm); + sk_process_cmsg4_pktinfo(s, cm); + sk_process_cmsg4_ttl(s, cm); } + + if ((cm->cmsg_level == IPPROTO_IPV6) && sk_is_ipv6(s)) + { + sk_process_cmsg6_pktinfo(s, cm); + sk_process_cmsg6_ttl(s, cm); + } + } } + /* static void sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) @@ -794,14 +848,33 @@ sk_setup(sock *s) if (s->priority >= 0) sk_set_priority(s, s->priority); - // XXXX better error handling if (s->ttl >= 0) - sk_set_ttl(s, s->ttl); + if (sk_set_ttl(s, s->ttl) < 0) + ERR("sk_set_ttl"); if (sk_is_ipv4(s)) - err = sk_request_pktinfo4(s); - else - err = sk_request_pktinfo6(s); + { + if (s->flags & SKF_LADDR_RX) + if (err = sk_request_cmsg4_pktinfo(s)) + goto bad; + + if (s->flags & SKF_TTL_RX) + if (err = sk_request_cmsg4_ttl(s)) + goto bad; + } + + if (sk_is_ipv6(s)) + { + if (s->flags & SKF_LADDR_RX) + if (err = sk_request_cmsg6_pktinfo(s)) + goto bad; + + if (s->flags & SKF_TTL_RX) + if (err = sk_request_cmsg6_ttl(s)); + } + + return NULL; + bad: return err; } @@ -1493,7 +1566,7 @@ sk_read(sock *s) } s->rpos = s->rbuf + e; sockaddr_read(sa, &s->faddr, NULL, &s->fport, 1); - sk_process_rx_cmsgs(s, &msg); + sk_process_cmsgs(s, &msg); s->rx_hook(s, e); return 1;