From: Philippe Guibert Date: Thu, 7 Dec 2017 17:13:54 +0000 (+0100) Subject: zebra: upon NS creation, collect the NSID via netlink X-Git-Tag: frr-5.0-dev~209^2~26 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=05895ad0be6c8fadcd895c3b1aa137b89791fbde;p=mirror%2Ffrr.git zebra: upon NS creation, collect the NSID via netlink A NS identifier is collected by netlink. This identifier is a 32 bit identifier that is either generated by the kernel (if not set) or manually set by a set netlink command. The commit here is getting the NSID from the newly created NS. If the linux option to create or get a new NSID from the kernel does not exist, then the NSID is locally genrated. Signed-off-by: Philippe Guibert --- diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h new file mode 100644 index 0000000000..9a92b7e14a --- /dev/null +++ b/include/linux/net_namespace.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2015 6WIND S.A. + * Author: Nicolas Dichtel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#ifndef _LINUX_NET_NAMESPACE_H_ +#define _LINUX_NET_NAMESPACE_H_ + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + NETNSA_NONE, +#define NETNSA_NSID_NOT_ASSIGNED -1 + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + __NETNSA_MAX, +}; + +#define NETNSA_MAX (__NETNSA_MAX - 1) + +#endif /* _LINUX_NET_NAMESPACE_H_ */ diff --git a/include/subdir.am b/include/subdir.am index 98bd148002..7a12b2ffae 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -6,4 +6,5 @@ noinst_HEADERS += \ include/linux/neighbour.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ + include/linux/net_namespace.h \ # end diff --git a/zebra/subdir.am b/zebra/subdir.am index 3474823623..fcfb0cc203 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -65,6 +65,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ zebra/zserv.c \ + zebra/zebra_netns_id.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -104,6 +105,7 @@ noinst_HEADERS += \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ zebra/zserv.h \ + zebra/zebra_netns_id.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c new file mode 100644 index 0000000000..c5e792bd77 --- /dev/null +++ b/zebra/zebra_netns_id.c @@ -0,0 +1,314 @@ +/* zebra NETNS ID handling routines + * those routines are implemented locally to avoid having external dependencies. + * Copyright (C) 2018 6WIND + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "ns.h" +#include "log.h" + +#if defined(HAVE_NETLINK) + +#include +#include +#include + +#include "rib.h" +#include "zebra_ns.h" +#include "kernel_netlink.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_id.h" + +/* in case NEWNSID not available, the NSID will be locally obtained + */ +#define NS_BASE_NSID 0 + +#if defined(HAVE_NETLINK) + +#define NETLINK_SOCKET_BUFFER_SIZE 512 +#define NETLINK_ALIGNTO 4 +#define NETLINK_ALIGN(len) (((len)+NETLINK_ALIGNTO-1) \ + & ~(NETLINK_ALIGNTO-1)) +#define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b) + +#endif /* defined(HAVE_NETLINK) */ + +static ns_id_t zebra_ns_id_get_fallback(const char *netnspath) +{ + static int zebra_ns_id_local; + + return zebra_ns_id_local++; +} + +#if defined(HAVE_NETLINK) + +static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type) +{ + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr *)buf; + nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr)); + + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + if (type == RTM_NEWNSID) + nlh->nlmsg_flags |= NLM_F_ACK; + nlh->nlmsg_seq = *seq = time(NULL); + return nlh; +} + +static int send_receive(int sock, struct nlmsghdr *nlh, + unsigned int seq, char *buf) +{ + int ret; + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + + ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0, + (struct sockaddr *) &snl, (socklen_t)sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) sendmsg() error: %s", + sock, safe_strerror(errno)); + return -1; + } + + /* reception */ + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = NETLINK_SOCKET_BUFFER_SIZE, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + zlog_err("netlink recvmsg: error %d (errno %u)", ret, errno); + return -1; + } + if (msg.msg_flags & MSG_TRUNC) { + zlog_err("netlink recvmsg : error message truncated"); + return -1; + } + /* nlh already points to buf */ + if (nlh->nlmsg_seq != seq) { + zlog_err("netlink recvmsg: bad sequence number %x (expected %x)", + seq, nlh->nlmsg_seq); + return -1; + } + return ret; +} + +/* extract on a valid nlmsg the nsid + * valid nlmsghdr - not a nlmsgerr + */ +static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) +{ + ns_id_t ns_id = NS_UNKNOWN; + int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) + + NETLINK_ALIGN(sizeof(struct rtgenmsg)); + int curr_length = offset; + void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); + struct nlattr *attr; + + for (attr = (struct nlattr *)((char *)buf + offset); + NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) && + attr->nla_len >= sizeof(struct nlattr) && + attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr); + attr += NETLINK_ALIGN(attr->nla_len)) { + curr_length += attr->nla_len; + if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { + u_int32_t *ptr = (u_int32_t *)(attr); + + ns_id = ptr[1]; + break; + } + } + return ns_id; +} + +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + int ns_id = -1; + struct sockaddr_nl snl; + int fd, sock, ret; + unsigned int seq; + ns_id_t return_nsid = NS_UNKNOWN; + + /* netns path check */ + if (!netnspath) + return NS_UNKNOWN; + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + + /* netlink socket */ + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + zlog_err("netlink( %u) socket() error: %s", + sock, safe_strerror(errno)); + return NS_UNKNOWN; + } + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTNLGRP_NSID; + snl.nl_pid = 0; /* AUTO PID */ + ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) socket() bind error: %s", + sock, safe_strerror(errno)); + close(sock); + close(fd); + return NS_UNKNOWN; + } + + /* message to send to netlink,and response : NEWNSID */ + char buf[NETLINK_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int len; + + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_NEWNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + + /* message to analyse : NEWNSID response */ + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + + ret = -1; + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + if (errno == 0) { + /* request NEWNSID was successfull + * return EEXIST error to get GETNSID + */ + errno = EEXIST; + } + } else { + /* other errors ignored + * attempt to get nsid + */ + ret = -1; + errno = EEXIST; + break; + } + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + + if (ret <= 0) { + if (errno != EEXIST && ret != 0) { + zlog_err("netlink( %u) recvfrom() error 2 when reading: %s", + fd, safe_strerror(errno)); + close(sock); + close(fd); + if (errno == ENOTSUP) { + zlog_warn("NEWNSID locally generated"); + return zebra_ns_id_get_fallback(netnspath); + } + return NS_UNKNOWN; + } + /* message to send to netlink : GETNSID */ + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_GETNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + break; + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + } + + close(fd); + close(sock); + return return_nsid; +} + +#else +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + return zebra_ns_id_get_fallback(netnspath); +} +#endif /* ! defined(HAVE_NETLINK) */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h new file mode 100644 index 0000000000..18fdf50cf1 --- /dev/null +++ b/zebra/zebra_netns_id.h @@ -0,0 +1,25 @@ +/* zebra NETNS ID handling routines + * Copyright (C) 2018 6WIND + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#if !defined(__ZEBRA_NS_ID_H__) +#define __ZEBRA_NS_ID_H__ +#include "zebra.h" +#include "ns.h" + +extern ns_id_t zebra_ns_id_get(const char *netnspath); + +#endif /* __ZEBRA_NS_ID_H__ */