From 4263106728eb229cfe7886af57efbea353ca2cfa Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 1 Nov 2013 16:58:48 -0700 Subject: add veth link support Signed-off-by: Cong Wang Signed-off-by: Thomas Graf --- include/linux/if_link.h | 8 ++ include/netlink/route/link.h | 2 + include/netlink/route/link/veth.h | 35 +++++ lib/Makefile.am | 2 +- lib/route/link.c | 2 +- lib/route/link/veth.c | 281 ++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 2 + tests/test-create-veth.c | 41 ++++++ 8 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 include/netlink/route/link/veth.h create mode 100644 lib/route/link/veth.c create mode 100644 tests/test-create-veth.c diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 565b7f6..4a7cace 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -312,6 +312,14 @@ struct ifla_vxlan_port_range { __be16 high; }; +enum { + VETH_INFO_UNSPEC, + VETH_INFO_PEER, + + __VETH_INFO_MAX +#define VETH_INFO_MAX (__VETH_INFO_MAX - 1) +}; + /* SR-IOV virtual function management section */ enum { diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h index fd7dda1..b0430f8 100644 --- a/include/netlink/route/link.h +++ b/include/netlink/route/link.h @@ -97,6 +97,8 @@ typedef enum { #define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1) +extern struct nla_policy link_policy[]; + extern struct rtnl_link *rtnl_link_alloc(void); extern void rtnl_link_put(struct rtnl_link *); diff --git a/include/netlink/route/link/veth.h b/include/netlink/route/link/veth.h new file mode 100644 index 0000000..7ca7d32 --- /dev/null +++ b/include/netlink/route/link/veth.h @@ -0,0 +1,35 @@ +/* + * netlink/route/link/veth.h VETH interface + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_LINK_VETH_H_ +#define NETLINK_LINK_VETH_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_veth_alloc(void); +extern void rtnl_link_veth_release(struct rtnl_link *); + +extern int rtnl_link_is_veth(struct rtnl_link *); + +extern struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *); +extern int rtnl_link_veth_add(struct nl_sock *sock, const char *name, + const char *peer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index b400a62..af2aa55 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -76,7 +76,7 @@ libnl_route_3_la_SOURCES = \ route/link/api.c route/link/vlan.c route/link/dummy.c \ route/link/bridge.c route/link/inet6.c route/link/inet.c \ route/link/bonding.c route/link/can.c route/link/macvlan.c \ - route/link/vxlan.c \ + route/link/vxlan.c route/link/veth.c \ \ route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \ route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \ diff --git a/lib/route/link.c b/lib/route/link.c index 37938aa..b758013 100644 --- a/lib/route/link.c +++ b/lib/route/link.c @@ -260,7 +260,7 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src) return 0; } -static struct nla_policy link_policy[IFLA_MAX+1] = { +struct nla_policy link_policy[IFLA_MAX+1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MTU] = { .type = NLA_U32 }, diff --git a/lib/route/link/veth.c b/lib/route/link/veth.c new file mode 100644 index 0000000..dba1294 --- /dev/null +++ b/lib/route/link/veth.c @@ -0,0 +1,281 @@ +/* + * lib/route/link/veth.c Virtual Ethernet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup link + * @defgroup veth VETH + * Virtual Ethernet + * + * @details + * \b Link Type Name: "veth" + * + * @route_doc{link_veth, VETH Documentation} + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct nla_policy veth_policy[VETH_INFO_MAX+1] = { + [VETH_INFO_PEER] = { .minlen = sizeof(struct ifinfomsg) }, +}; + +static int veth_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[VETH_INFO_MAX+1]; + struct nlattr *peer_tb[IFLA_MAX + 1]; + struct rtnl_link *peer = link->l_info; + int err; + + NL_DBG(3, "Parsing veth link info"); + + if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0) + goto errout; + + if (tb[VETH_INFO_PEER]) { + struct nlattr *nla_peer; + struct ifinfomsg *ifi; + + nla_peer = tb[VETH_INFO_PEER]; + ifi = nla_data(nla_peer); + + peer->l_family = ifi->ifi_family; + peer->l_arptype = ifi->ifi_type; + peer->l_index = ifi->ifi_index; + peer->l_flags = ifi->ifi_flags; + peer->l_change = ifi->ifi_change; + err = nla_parse(peer_tb, IFLA_MAX, + nla_data(nla_peer) + sizeof(struct ifinfomsg), + nla_len(nla_peer) - sizeof(struct ifinfomsg), + link_policy); + if (err < 0) + goto errout; + + err = rtnl_link_info_parse(peer, peer_tb); + if (err < 0) + goto errout; + } + + err = 0; + +errout: + return err; +} + +static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ +} + +static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct rtnl_link *peer = link->l_info; + char *name; + name = rtnl_link_get_name(peer); + nl_dump(p, " peer "); + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", peer->l_index); +} + +static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct rtnl_link *dst_peer , *src_peer = src->l_info; + int err; + + dst_peer = dst->l_info = rtnl_link_alloc(); + if (!dst_peer || !src_peer) + return -NLE_NOMEM; + if ((err = rtnl_link_set_type(dst, "veth")) < 0) { + rtnl_link_put(dst_peer); + return err; + } + + memcpy(dst_peer, src_peer, sizeof(struct rtnl_link)); + + return 0; +} + +static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct rtnl_link *peer = link->l_info; + struct ifinfomsg ifi; + struct nlattr *data, *info_peer; + + memset(&ifi, 0, sizeof ifi); + ifi.ifi_family = peer->l_family; + ifi.ifi_type = peer->l_arptype; + ifi.ifi_index = peer->l_index; + ifi.ifi_flags = peer->l_flags; + ifi.ifi_change = peer->l_change; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER))) + return -NLE_MSGSIZE; + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + return -NLE_MSGSIZE; + rtnl_link_fill_info(msg, peer); + nla_nest_end(msg, info_peer); + nla_nest_end(msg, data); + + return 0; +} + +static struct rtnl_link_info_ops veth_info_ops = { + .io_name = "veth", + .io_parse = veth_parse, + .io_dump = { + [NL_DUMP_LINE] = veth_dump_line, + [NL_DUMP_DETAILS] = veth_dump_details, + }, + .io_clone = veth_clone, + .io_put_attrs = veth_put_attrs, +}; + +/** @cond SKIP */ + +#define IS_VETH_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &veth_info_ops) { \ + APPBUG("Link is not a veth link. set type \"veth\" first."); \ + return NULL; \ + } +/** @endcond */ + +/** + * @name VETH Object + * @{ + */ + +/** + * Allocate link object of type veth + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_veth_alloc(void) +{ + struct rtnl_link *link, *peer; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + if (!(peer = rtnl_link_alloc())) { + rtnl_link_put(link); + return NULL; + } + + if ((err = rtnl_link_set_type(link, "veth")) < 0) { + rtnl_link_put(peer); + rtnl_link_put(link); + return NULL; + } + if ((err = rtnl_link_set_type(peer, "veth")) < 0) { + rtnl_link_put(peer); + rtnl_link_put(link); + return NULL; + } + + link->l_info = peer; + peer->l_info = link; + return link; +} + +/** + * Get the peer link of a veth link + * + * @return the peer link object. + */ +struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link) +{ + IS_VETH_LINK_ASSERT(link); + return link->l_info; +} + +/** + * Release a veth link and its peer + * + */ +void rtnl_link_veth_release(struct rtnl_link *link) +{ + struct rtnl_link *peer = rtnl_link_veth_get_peer(link); + rtnl_link_put(peer); + rtnl_link_put(link); +} + +/** + * Check if link is a veth link + * @arg link Link object + * + * @return True if link is a veth link, otherwise false is returned. + */ +int rtnl_link_is_veth(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth"); +} + +/** + * Create a new kernel veth device + * @arg sock netlink socket + * @arg name name of the veth device or NULL + * @arg peer_name name of its peer or NULL + * + * Creates a new veth device pair in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "veth%d" (e.g. veth0, veth1, etc.) + * + * @return 0 on success or a negative error code + */ +int rtnl_link_veth_add(struct nl_sock *sock, const char *name, + const char *peer_name) +{ + struct rtnl_link *link, *peer; + int err = -NLE_NOMEM; + + if (!(link = rtnl_link_veth_alloc())) + return -NLE_NOMEM; + peer = rtnl_link_veth_get_peer(link); + + if (name && peer_name) { + rtnl_link_set_name(link, name); + rtnl_link_set_name(peer, peer_name); + } + + err = rtnl_link_add(sock, link, NLM_F_CREATE); + + rtnl_link_put(peer); + rtnl_link_put(link); + + return err; +} + +/** @} */ + +static void __init veth_init(void) +{ + rtnl_link_register_info(&veth_info_ops); +} + +static void __exit veth_exit(void) +{ + rtnl_link_unregister_info(&veth_info_ops); +} + +/** @} */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 9d72b88..3e1af27 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,7 @@ check_PROGRAMS = \ test-create-bond \ test-create-vlan \ test-create-vxlan \ + test-create-veth \ test-create-bridge \ test-delete-link \ test-socket-creation \ @@ -43,6 +44,7 @@ test_cache_mngr_SOURCES = test-cache-mngr.c test_create_bond_SOURCES = test-create-bond.c test_create_vlan_SOURCES = test-create-vlan.c test_create_vxlan_SOURCES = test-create-vxlan.c +test_create_veth_SOURCES = test-create-veth.c test_create_bridge_SOURCES = test-create-bridge.c test_delete_link_SOURCES = test-delete-link.c test_genl_SOURCES = test-genl.c diff --git a/tests/test-create-veth.c b/tests/test-create-veth.c new file mode 100644 index 0000000..038353f --- /dev/null +++ b/tests/test-create-veth.c @@ -0,0 +1,41 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int err; + struct rtnl_link *peer; + + sk = nl_socket_alloc(); + if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) { + nl_perror(err, "Unable to connect socket"); + return err; + } + +#if 0 + rtnl_link_veth_add(sk, "veth2", "veth3"); +#else + link = rtnl_link_veth_alloc(); + if (!link) { + nl_perror(err, "Unable to alloc link"); + return err; + } + + rtnl_link_set_name(link, "veth8"); + peer = rtnl_link_veth_get_peer(link); + rtnl_link_set_name(peer, "veth9"); + + if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) { + nl_perror(err, "Unable to add link"); + return err; + } + printf("peer is %s\n", rtnl_link_get_name(peer)); + rtnl_link_put(link); +#endif + nl_close(sk); + + return 0; +} -- cgit v0.12