From 244c1cdce553f6b722920ffe40a492ca230ad086 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 19 May 2015 17:40:44 -0700 Subject: [PATCH] ptm-integration.patch Integrates Prescriptive Topology Module(ptm) into quagga. If this module is enabled, link ups are notified only after the link is verified as being connected to the neighbor specified. The neighbor specification and checking is done by the ptm daemon. --- lib/if.c | 18 ++- lib/if.h | 4 + lib/zclient.c | 2 + vtysh/Makefile.am | 3 +- zebra/Makefile.am | 4 +- zebra/interface.c | 31 ++++- zebra/main.c | 7 + zebra/redistribute.c | 9 +- zebra/rt_netlink.c | 6 +- zebra/zebra_ptm.c | 308 +++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_ptm.h | 34 +++++ zebra/zserv.c | 2 + 12 files changed, 414 insertions(+), 14 deletions(-) create mode 100644 zebra/zebra_ptm.c create mode 100644 zebra/zebra_ptm.h diff --git a/lib/if.c b/lib/if.c index 3425e3a8b1..b3decf3be9 100644 --- a/lib/if.c +++ b/lib/if.c @@ -38,6 +38,7 @@ /* Master list of interfaces. */ struct list *iflist; +int ptm_enable = 0; /* One for each program. This structure is needed to store hooks. */ struct if_master @@ -373,12 +374,25 @@ if_is_running (struct interface *ifp) } /* Is the interface operative, eg. either UP & RUNNING - or UP & !ZEBRA_INTERFACE_LINK_DETECTION */ + or UP & !ZEBRA_INTERFACE_LINK_DETECTION and + if ptm checking is enabled, then ptm check has passed */ int if_is_operative (struct interface *ifp) { return ((ifp->flags & IFF_UP) && - (ifp->flags & IFF_RUNNING || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))); + (((ifp->flags & IFF_RUNNING) && + (ifp->ptm_status || !ifp->ptm_enable)) || + !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))); +} + +/* Is the interface operative, eg. either UP & RUNNING + or UP & !ZEBRA_INTERFACE_LINK_DETECTION, without PTM check */ +int +if_is_no_ptm_operative (struct interface *ifp) +{ + return ((ifp->flags & IFF_UP) && + ((ifp->flags & IFF_RUNNING) || + !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))); } /* Is this loopback interface ? */ diff --git a/lib/if.h b/lib/if.h index 80b62e6e6c..287d26cf97 100644 --- a/lib/if.h +++ b/lib/if.h @@ -135,6 +135,9 @@ struct interface /* Daemon specific interface data pointer. */ void *info; + char ptm_enable; /* Should we look at ptm_status ? */ + char ptm_status; + /* Statistics fileds. */ #ifdef HAVE_PROC_NET_DEV struct if_stats stats; @@ -285,6 +288,7 @@ extern void if_delete (struct interface *); extern int if_is_up (struct interface *); extern int if_is_running (struct interface *); extern int if_is_operative (struct interface *); +extern int if_is_no_ptm_operative (struct interface *); extern int if_is_loopback (struct interface *); extern int if_is_broadcast (struct interface *); extern int if_is_pointopoint (struct interface *); diff --git a/lib/zclient.c b/lib/zclient.c index fab130f646..8b4597714e 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -729,6 +729,8 @@ zebra_interface_if_set_value (struct stream *s, struct interface *ifp) /* Read interface's value. */ ifp->flags = stream_getq (s); + ifp->ptm_enable = stream_getc (s); + ifp->ptm_status = stream_getc (s); ifp->metric = stream_getl (s); ifp->mtu = stream_getl (s); ifp->mtu6 = stream_getl (s); diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 5c325ec261..9652a92e0a 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -34,7 +34,8 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ $(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \ $(top_srcdir)/zebra/zebra_routemap.c \ - $(top_srcdir)/zebra/zebra_fpm.c + $(top_srcdir)/zebra/zebra_fpm.c \ + $(top_srcdir)/zebra/zebra_ptm.c vtysh_cmd.c: $(vtysh_cmd_FILES) ./$(EXTRA_DIST) $(vtysh_cmd_FILES) > vtysh_cmd.c diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 9aa135236b..03f9cc144f 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -34,10 +34,10 @@ zebra_SOURCES = \ zserv.c main.c interface.c connected.c zebra_rib.c zebra_routemap.c \ redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ - $(othersrc) zebra_rnh.c + $(othersrc) zebra_ptm.c zebra_rnh.c testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ - zebra_vty.c \ + zebra_vty.c zebra_ptm.c \ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c noinst_HEADERS = \ diff --git a/zebra/interface.c b/zebra/interface.c index fe5e3e2be8..10344c6281 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -40,6 +40,9 @@ #include "zebra/redistribute.h" #include "zebra/debug.h" #include "zebra/irdp.h" +#include "zebra/zebra_ptm.h" + +#define ZEBRA_PTM_SUPPORT #ifdef RTADV /* Order is intentional. Matches RFC4191. This array is also used for @@ -58,6 +61,7 @@ if_zebra_new_hook (struct interface *ifp) zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC; zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF; + ifp->ptm_enable = ptm_enable; #ifdef RTADV { /* Set default router advertise values. */ @@ -261,7 +265,7 @@ if_flags_update (struct interface *ifp, uint64_t newflags) { if_flags_mangle (ifp, &newflags); - if (if_is_operative (ifp)) + if (if_is_no_ptm_operative (ifp)) { /* operative -> inoperative? */ ifp->flags = newflags; @@ -533,8 +537,14 @@ if_up (struct interface *ifp) struct prefix *p; /* Notify the protocol daemons. */ + if (ifp->ptm_enable && !ifp->ptm_status) { + zlog_warn("%s: interface %s hasn't passed ptm check\n", __func__, + ifp->name); + return; + } zebra_interface_up_update (ifp); + /* Install connected routes to the kernel. */ if (ifp->connected) { @@ -742,6 +752,17 @@ if_dump_vty (struct vty *vty, struct interface *ifp) vty_out (vty, "down%s", VTY_NEWLINE); } + vty_out (vty, " PTM status: "); + if (ifp->ptm_enable) { + if (ifp->ptm_status) { + vty_out (vty, "pass%s", VTY_NEWLINE); + } else { + vty_out (vty, "fail%s", VTY_NEWLINE); + } + } else { + vty_out (vty, "disabled%s", VTY_NEWLINE); + } + if (ifp->desc) vty_out (vty, " Description: %s%s", ifp->desc, VTY_NEWLINE); @@ -1079,11 +1100,11 @@ DEFUN (linkdetect, int if_was_operative; ifp = (struct interface *) vty->index; - if_was_operative = if_is_operative(ifp); + if_was_operative = if_is_no_ptm_operative(ifp); SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); /* When linkdetection is enabled, if might come down */ - if (!if_is_operative(ifp) && if_was_operative) if_down(ifp); + if (!if_is_no_ptm_operative(ifp) && if_was_operative) if_down(ifp); /* FIXME: Will defer status change forwarding if interface does not come down! */ @@ -1102,7 +1123,7 @@ DEFUN (no_linkdetect, int if_was_operative; ifp = (struct interface *) vty->index; - if_was_operative = if_is_operative(ifp); + if_was_operative = if_is_no_ptm_operative(ifp); UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); /* Interface may come up after disabling link detection */ @@ -1570,6 +1591,8 @@ if_config_write (struct vty *vty) struct listnode *node; struct interface *ifp; + zebra_ptm_write (vty); + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) { struct zebra_if *if_data; diff --git a/zebra/main.c b/zebra/main.c index 306468fce3..13ad08eff5 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -40,6 +40,9 @@ #include "zebra/irdp.h" #include "zebra/rtadv.h" #include "zebra/zebra_fpm.h" +#include "zebra/zebra_ptm.h" + +#define ZEBRA_PTM_SUPPORT /* Zebra instance */ struct zebra_t zebrad = @@ -334,6 +337,10 @@ main (int argc, char **argv) #ifdef HAVE_IRDP irdp_init(); #endif + /* PTM socket */ +#ifdef ZEBRA_PTM_SUPPORT + zebra_ptm_init(); +#endif /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 078c2ad0b3..4767d9dbfe 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -37,6 +37,9 @@ #include "zebra/debug.h" #include "zebra/router-id.h" +#define ZEBRA_PTM_SUPPORT + + /* master zebra server structure */ extern struct zebra_t zebrad; @@ -292,8 +295,10 @@ zebra_interface_up_update (struct interface *ifp) if (IS_ZEBRA_DEBUG_EVENT) zlog_debug ("MESSAGE: ZEBRA_INTERFACE_UP %s", ifp->name); - for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) - zsend_interface_update (ZEBRA_INTERFACE_UP, client, ifp); + if (ifp->ptm_status || !ifp->ptm_enable) { + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + zsend_interface_update (ZEBRA_INTERFACE_UP, client, ifp); + } } /* Interface down information. */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 290c8bc282..ea2d42ab3b 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1114,12 +1114,12 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) netlink_interface_update_hw_addr (tb, ifp); - if (if_is_operative (ifp)) + if (if_is_no_ptm_operative (ifp)) { ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (!if_is_operative (ifp)) + if (!if_is_no_ptm_operative (ifp)) if_down (ifp); - else + else if (if_is_operative (ifp)) /* Must notify client daemons of new interface status. */ zebra_interface_up_update (ifp); } diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c new file mode 100644 index 0000000000..6201b60f4e --- /dev/null +++ b/zebra/zebra_ptm.c @@ -0,0 +1,308 @@ +/* Kernel routing table updates using netlink over GNU/Linux system. + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include /* for sockaddr_un */ +#include +#include "zebra/zserv.h" +#include "zebra/interface.h" +#include "zebra/debug.h" +#include "zebra/zebra_ptm.h" +#include "if.h" +#include "command.h" + +#define ZEBRA_PTM_RECONNECT_TIME_INITIAL 1 /* initial reconnect is 1s */ +#define ZEBRA_PTM_RECONNECT_TIME_MAX 300 +extern struct zebra_t zebrad; +int ptm_enable; + +int zebra_ptm_sock = -1; +struct thread *zebra_ptm_thread = NULL; + +static int zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL; + +static void zebra_ptm_finish(void); +static int zebra_ptm_socket_init(void); +static void zebra_ptm_process_msg(char *msg); +int zebra_ptm_sock_read(struct thread *); +static void zebra_ptm_install_commands (void); + +const char ZEBRA_PTM_SOCK_NAME[] = "\0/var/run/ptmd.socket"; + +typedef enum ptm_msg_type { + PTM_LLDP = 0, + PTM_BFD, + PTM_MAX +} ptm_msg_t; + +void +zebra_ptm_init (void) +{ + zebra_ptm_install_commands(); +} + +int +zebra_ptm_connect (struct thread *t) +{ + zebra_ptm_socket_init(); + + if (zebra_ptm_sock != -1) { + zebra_ptm_thread = thread_add_read (zebrad.master, zebra_ptm_sock_read, NULL, zebra_ptm_sock); + zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL; + } else { + zlog_err("%s: Socket connect to %s failed with err = %d\n", __func__, + ZEBRA_PTM_SOCK_NAME, errno); + zebra_ptm_reconnect_time *= 2; + if (zebra_ptm_reconnect_time > ZEBRA_PTM_RECONNECT_TIME_MAX) + zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_MAX; + + zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect, NULL, + zebra_ptm_reconnect_time); + } + + return(errno); +} + +static void +zebra_ptm_finish (void) +{ + if (zebra_ptm_sock != -1) + { + if (zebra_ptm_thread != NULL) + { + thread_cancel(zebra_ptm_thread); + zebra_ptm_thread = NULL; + } + close (zebra_ptm_sock); + zebra_ptm_sock = -1; + } +} + +DEFUN (zebra_ptm_enable, + zebra_ptm_enable_cmd, + "ptm-enable", + "Enable neighbor check with specified topology\n") +{ + struct listnode *i; + struct interface *ifp; + + ptm_enable = 1; + + for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp)) + if (!ifp->ptm_enable) + { + ifp->ptm_enable = 1; + ifp->ptm_status = 1; /* to bring down ports that may fail check */ + } + + zebra_ptm_connect(NULL); + + return CMD_SUCCESS; +} + +DEFUN (no_zebra_ptm_enable, + no_zebra_ptm_enable_cmd, + "no ptm-enable", + NO_STR + "Enable neighbor check with specified topology\n") +{ + struct listnode *i; + struct interface *ifp; + int send_linkup; + + ptm_enable = 0; + for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp)) + { + if (ifp->ptm_enable) + { + if (!if_is_operative(ifp)) + send_linkup = 1; + + ifp->ptm_enable = 0; + if (if_is_operative (ifp) && send_linkup) { + zlog_debug ("%s: Bringing up interface %s\n", __func__, + ifp->name); + if_up (ifp); + } + } + } + zebra_ptm_finish(); + + return CMD_SUCCESS; +} + +void +zebra_ptm_write (struct vty *vty) +{ + if (ptm_enable) + vty_out (vty, "ptm-enable%s", VTY_NEWLINE); + + return; +} + +static int +zebra_ptm_socket_init (void) +{ + int ret; + int sock; + struct sockaddr_un addr; + + zebra_ptm_sock = -1; + sock = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* Make server socket. */ + memset (&addr, 0, sizeof (struct sockaddr_un)); + addr.sun_family = AF_UNIX; + memcpy (&addr.sun_path, ZEBRA_PTM_SOCK_NAME, + sizeof(ZEBRA_PTM_SOCK_NAME)); + + ret = connect(sock, (struct sockaddr *) &addr, + sizeof (addr.sun_family)+sizeof (ZEBRA_PTM_SOCK_NAME)-1); + if (ret < 0) + { + zlog_err("%s: Unable to connect to socket %s, errno=%d\n", + __func__, ZEBRA_PTM_SOCK_NAME, errno); + close (sock); + return -1; + } + zlog_debug ("%s: connection to ptm socket %s succeeded\n", + __func__, ZEBRA_PTM_SOCK_NAME); + zebra_ptm_sock = sock; + return sock; +} + +static void +zebra_ptm_install_commands (void) +{ + install_element (CONFIG_NODE, &zebra_ptm_enable_cmd); + install_element (CONFIG_NODE, &no_zebra_ptm_enable_cmd); +} + +static void +zebra_ptm_process_msg (char *buf) +{ + char port_name[IF_NAMESIZE+1]; + char status[8]; + char tgt_ip[12]; + char type[2]; + char byte_len[4]; + struct interface *ifp; + int scan_count, bytes_read; + char *pos; + const char *delim = "\n"; + struct in_addr dest_addr; + struct prefix dest_prefix; + ptm_msg_t msg_type; + + /* the messages from the ptm ctl socket are in text only */ + /* with a fixed format: */ + pos = strtok(buf, delim); + while (pos != NULL) { + if (strstr(pos, "EOF") != NULL) + break; + scan_count = sscanf(pos, "%3s %16s %1s %4s %n", byte_len, port_name, type, status, &bytes_read); + + if (scan_count == 4) { + + zlog_debug("%s: %s received new status %s, type %s with scan count = %d\n", + __func__, port_name, type, status, scan_count); + + ifp = if_lookup_by_name(port_name); + if (ifp == NULL) { + zlog_err("%s: %s not found in interface list\n", __func__, port_name); + return; + } + + if (strchr(type, "B") == 0) { + msg_type = PTM_BFD; + pos = pos + bytes_read; + scan_count = sscanf(pos, "%11s", tgt_ip); + } else { + msg_type = PTM_LLDP; + } + + if (strcmp(status, "pass") == 0) { + if (!ifp->ptm_status) { + ifp->ptm_status = 1; + if (ifp->ptm_enable && if_is_no_ptm_operative (ifp)) + if_up (ifp); + } + } else if (strcmp (status, "fail") == 0) { + if (ifp->ptm_status) { + ifp->ptm_status = 0; + if (ifp->ptm_enable && if_is_no_ptm_operative (ifp)) { + if (msg_type == PTM_BFD) { + + if (inet_pton(AF_INET, tgt_ip, &dest_addr) <= 0) { + zlog_err ("%s: Not a valid destination address: %s", + __func__, tgt_ip); + return; + } + dest_prefix.family = AF_INET; + dest_prefix.u.prefix4 = dest_addr; + dest_prefix.prefixlen = IPV4_MAX_PREFIXLEN; + + /* Send BFD message with ifp and dest_prefix to protocols */ + } else { + if_down (ifp); + } + } + } + } + } + pos = strtok(NULL, delim); + } +} + +int +zebra_ptm_sock_read (struct thread *thread) +{ + int sock; + char rcvbuf[ZEBRA_PTM_MAX_SOCKBUF]; + int nbytes; + + sock = THREAD_FD (thread); + + if (sock == -1) + return -1; + + nbytes = recv(sock, rcvbuf, sizeof(rcvbuf), 0); + + if (nbytes <= 0) + { + if (nbytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN) + zlog_warn ("routing socket error: %s", safe_strerror (errno)); + + zebra_ptm_sock = -1; + zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect, NULL, + zebra_ptm_reconnect_time); + return (-1); + } + + zlog_debug ("%s: Received message \n%s\n", __func__, rcvbuf); + zebra_ptm_thread = thread_add_read (zebrad.master, zebra_ptm_sock_read, NULL, sock); + + zebra_ptm_process_msg (rcvbuf); + + return(0); +} diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h new file mode 100644 index 0000000000..126c44811b --- /dev/null +++ b/zebra/zebra_ptm.h @@ -0,0 +1,34 @@ +/* + * Definitions for prescriptive topology module (PTM). + * Copyright (C) 1998, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_PTM_H +#define _ZEBRA_PTM_H + +extern const char ZEBRA_PTM_SOCK_NAME[]; +#define ZEBRA_PTM_MAX_SOCKBUF 3200 /* 25B *128 ports */ +extern int ptm_enable; + +void zebra_ptm_init (void); +int zebra_ptm_connect (struct thread *t); +void zebra_ptm_write (struct vty *vty); + +#endif diff --git a/zebra/zserv.c b/zebra/zserv.c index 98b6f91c9b..f4ce81f788 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -150,6 +150,8 @@ zserv_encode_interface (struct stream *s, struct interface *ifp) stream_putl (s, ifp->ifindex); stream_putc (s, ifp->status); stream_putq (s, ifp->flags); + stream_putc (s, ifp->ptm_enable); + stream_putc (s, ifp->ptm_status); stream_putl (s, ifp->metric); stream_putl (s, ifp->mtu); stream_putl (s, ifp->mtu6); -- 2.39.5