Skip to content

Commit

Permalink
zebra: upon NS creation, collect the NSID via netlink
Browse files Browse the repository at this point in the history
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 <philippe.guibert@6wind.com>
  • Loading branch information
pguibert6WIND committed Feb 27, 2018
1 parent ce1be36 commit 05895ad
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 0 deletions.
23 changes: 23 additions & 0 deletions include/linux/net_namespace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Copyright (c) 2015 6WIND S.A.
* Author: Nicolas Dichtel <nicolas.dichtel@6wind.com>
*
* 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_ */
1 change: 1 addition & 0 deletions include/subdir.am
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ noinst_HEADERS += \
include/linux/neighbour.h \
include/linux/rtnetlink.h \
include/linux/socket.h \
include/linux/net_namespace.h \
# end
2 changes: 2 additions & 0 deletions zebra/subdir.am
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 = \
Expand Down
314 changes: 314 additions & 0 deletions zebra/zebra_netns_id.c
Original file line number Diff line number Diff line change
@@ -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 <zebra.h>

#include "ns.h"
#include "log.h"

#if defined(HAVE_NETLINK)

#include <linux/net_namespace.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#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) */
25 changes: 25 additions & 0 deletions zebra/zebra_netns_id.h
Original file line number Diff line number Diff line change
@@ -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__ */

0 comments on commit 05895ad

Please sign in to comment.