diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/libfrr.c | 123 | ||||
| -rw-r--r-- | lib/libfrr.h | 4 | ||||
| -rw-r--r-- | lib/zclient.c | 114 | ||||
| -rw-r--r-- | lib/zclient.h | 10 |
4 files changed, 149 insertions, 102 deletions
diff --git a/lib/libfrr.c b/lib/libfrr.c index 31d93009fa..e5573da900 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -19,6 +19,7 @@ */ #include <zebra.h> +#include <sys/un.h> #include <sys/types.h> #include <sys/wait.h> @@ -45,6 +46,7 @@ char frr_protoname[256] = "NONE"; char frr_protonameinst[256] = "NONE"; char config_default[256]; +char frr_zclientpath[256]; static char pidfile_default[256]; static char vtypath_default[256]; @@ -135,6 +137,116 @@ static const struct optspec os_user = {"u:g:", lo_user}; +bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, + const char *path) +{ + memset(sa, 0, sizeof(*sa)); + + if (!path) + path = ZEBRA_SERV_PATH; + + if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) { + /* note: this functionality is disabled at bottom */ + int af; + int port = ZEBRA_PORT; + char *err = NULL; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; + + path += strlen(ZAPI_TCP_PATHNAME); + + switch (path[0]) { + case '4': + path++; + af = AF_INET; + break; + case '6': + path++; + /* fallthrough */ + default: + af = AF_INET6; + break; + } + + switch (path[0]) { + case '\0': + break; + case ':': + path++; + port = strtoul(path, &err, 10); + if (*err || !*path) + return false; + break; + default: + return false; + } + + sa->ss_family = af; + switch (af) { + case AF_INET: + sin = (struct sockaddr_in *)sa; + sin->sin_port = htons(port); + sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + *sa_len = sizeof(struct sockaddr_in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin->sin_len = *sa_len; +#endif + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + sin6->sin6_port = htons(port); + inet_pton(AF_INET6, "::1", &sin6->sin6_addr); + *sa_len = sizeof(struct sockaddr_in6); +#ifdef SIN6_LEN + sin6->sin6_len = *sa_len; +#endif + break; + } + +#if 1 + /* force-disable this path, because tcp-zebra is a + * SECURITY ISSUE. there are no checks at all against + * untrusted users on the local system connecting on TCP + * and injecting bogus routing data into the entire routing + * domain. + * + * The functionality is only left here because it may be + * useful during development, in order to be able to get + * tcpdump or wireshark watching ZAPI as TCP. If you want + * to do that, flip the #if 1 above to #if 0. */ + memset(sa, 0, sizeof(*sa)); + return false; +#endif + } else { + /* "sun" is a #define on solaris */ + struct sockaddr_un *suna = (struct sockaddr_un *)sa; + + suna->sun_family = AF_UNIX; + strlcpy(suna->sun_path, path, sizeof(suna->sun_path)); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + *sa_len = suna->sun_len = SUN_LEN(suna); +#else + *sa_len = sizeof(suna->sun_family) + strlen(suna->sun_path); +#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ +#if 0 + /* this is left here for future reference; Linux abstract + * socket namespace support can be enabled by replacing + * above #if 0 with #ifdef GNU_LINUX. + * + * THIS IS A SECURITY ISSUE, the abstract socket namespace + * does not have user/group permission control on sockets. + * we'd need to implement SCM_CREDENTIALS support first to + * check that only proper users can connect to abstract + * sockets. (same problem as tcp-zebra, except there is a + * fix with SCM_CREDENTIALS. tcp-zebra has no such fix.) + */ + if (suna->sun_path[0] == '@') + suna->sun_path[0] = '\0'; +#endif + } + return true; +} + static struct frr_daemon_info *di = NULL; void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) @@ -164,6 +276,8 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) strlcpy(frr_protoname, di->logname, sizeof(frr_protoname)); strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst)); + + strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath)); } void frr_opt_add(const char *optstr, const struct option *longopts, @@ -246,7 +360,7 @@ static int frr_opt(int opt) case 'z': if (di->flags & FRR_NO_ZCLIENT) return 1; - zclient_serv_path_set(optarg); + strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath)); break; case 'A': if (di->flags & FRR_NO_TCPVTY) @@ -399,6 +513,13 @@ struct thread_master *frr_init(void) zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl); #endif + if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len, + frr_zclientpath)) { + fprintf(stderr, "Invalid zserv socket path: %s\n", + frr_zclientpath); + exit(1); + } + /* don't mkdir these as root... */ if (!(di->flags & FRR_NO_PRIVSEP)) { if (!di->pid_file || !di->vty_path) diff --git a/lib/libfrr.h b/lib/libfrr.h index 238a6f976d..1710fc9a84 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -101,7 +101,11 @@ extern void frr_vty_serv(void); /* note: contains call to frr_vty_serv() */ extern void frr_run(struct thread_master *master); +extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, + const char *path); + extern char config_default[256]; +extern char frr_zclientpath[256]; extern const char frr_sysconfdir[]; extern const char frr_vtydir[]; extern const char frr_moduledir[]; diff --git a/lib/zclient.c b/lib/zclient.c index 893d769e3a..24cb699196 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -45,7 +45,8 @@ enum event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; /* Prototype for event manager. */ static void zclient_event(enum event, struct zclient *); -const char *zclient_serv_path = NULL; +struct sockaddr_storage zclient_addr; +socklen_t zclient_addr_len; /* This file local debug flag. */ int zclient_debug = 0; @@ -183,31 +184,28 @@ void zclient_reset(struct zclient *zclient) zclient_init(zclient, zclient->redist_default, zclient->instance); } -#ifdef HAVE_TCP_ZEBRA - -/* Make socket to zebra daemon. Return zebra socket. */ -static int zclient_socket(void) +/** + * Connect to zebra daemon. + * @param zclient a pointer to zclient structure + * @return socket fd just to make sure that connection established + * @see zclient_init + * @see zclient_new + */ +int zclient_socket_connect(struct zclient *zclient) { int sock; int ret; - struct sockaddr_in serv; /* We should think about IPv6 connection. */ - sock = socket(AF_INET, SOCK_STREAM, 0); + sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0); if (sock < 0) return -1; - /* Make server socket. */ - memset(&serv, 0, sizeof(struct sockaddr_in)); - serv.sin_family = AF_INET; - serv.sin_port = htons(ZEBRA_PORT); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - serv.sin_len = sizeof(struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + set_cloexec(sock); /* Connect to zebra. */ - ret = connect(sock, (struct sockaddr *)&serv, sizeof(serv)); + ret = connect(sock, (struct sockaddr *)&zclient_addr, + zclient_addr_len); if (ret < 0) { if (zclient_debug) zlog_warn("%s connect failure: %d(%s)", @@ -216,65 +214,11 @@ static int zclient_socket(void) close(sock); return -1; } - return sock; -} -#else - -/* For sockaddr_un. */ -#include <sys/un.h> - -static int zclient_socket_un(const char *path) -{ - int ret; - int sock, len; - struct sockaddr_un addr; - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - return -1; - - /* Make server socket. */ - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, strlen(path)); -#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN - len = addr.sun_len = SUN_LEN(&addr); -#else - len = sizeof(addr.sun_family) + strlen(addr.sun_path); -#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ - - ret = connect(sock, (struct sockaddr *)&addr, len); - if (ret < 0) { - if (zclient_debug) - zlog_warn("%s connect failure: %d(%s)", - __PRETTY_FUNCTION__, errno, - safe_strerror(errno)); - close(sock); - return -1; - } + zclient->sock = sock; return sock; } -#endif /* HAVE_TCP_ZEBRA */ - -/** - * Connect to zebra daemon. - * @param zclient a pointer to zclient structure - * @return socket fd just to make sure that connection established - * @see zclient_init - * @see zclient_new - */ -int zclient_socket_connect(struct zclient *zclient) -{ -#ifdef HAVE_TCP_ZEBRA - zclient->sock = zclient_socket(); -#else - zclient->sock = zclient_socket_un(zclient_serv_path_get()); -#endif - return zclient->sock; -} - static int zclient_failed(struct zclient *zclient) { zclient->fail++; @@ -2225,34 +2169,6 @@ static void zclient_event(enum event event, struct zclient *zclient) } } -const char *zclient_serv_path_get() -{ - return zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH; -} - -void zclient_serv_path_set(char *path) -{ - struct stat sb; - - /* reset */ - zclient_serv_path = NULL; - - /* test if `path' is socket. don't set it otherwise. */ - if (stat(path, &sb) == -1) { - zlog_warn("%s: zebra socket `%s' does not exist", __func__, - path); - return; - } - - if ((sb.st_mode & S_IFMT) != S_IFSOCK) { - zlog_warn("%s: `%s' is not unix socket, sir", __func__, path); - return; - } - - /* it seems that path is unix socket */ - zclient_serv_path = path; -} - void zclient_interface_set_master(struct zclient *client, struct interface *master, struct interface *slave) diff --git a/lib/zclient.h b/lib/zclient.h index 8a2729543f..5edb56f517 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -42,6 +42,14 @@ /* Zebra header size. */ #define ZEBRA_HEADER_SIZE 8 +/* special socket path name to use TCP + * @ is used as first character because that's abstract socket names on Linux + */ +#define ZAPI_TCP_PATHNAME "@tcp" + +extern struct sockaddr_storage zclient_addr; +extern socklen_t zclient_addr_len; + /* Zebra message types. */ typedef enum { ZEBRA_INTERFACE_ADD, @@ -306,8 +314,6 @@ extern void zclient_reset(struct zclient *); extern void zclient_free(struct zclient *); extern int zclient_socket_connect(struct zclient *); -extern void zclient_serv_path_set(char *path); -extern const char *zclient_serv_path_get(void); extern u_short *redist_check_instance(struct redist_proto *, u_short); extern void redist_add_instance(struct redist_proto *, u_short); |
